격리 수준은 ‘무엇을 허용하는가’의 목록이다
격리 수준을 외울 때 흔히 표를 통째로 암기한다. 하지만 이 표는 어떤 이상 현상을 허용할지를 정한 계약서에 가깝다. 낮출수록 동시성이 올라가고 이상 현상을 더 많이 감수한다.
Read Committed
커밋되지 않은 데이터는 읽지 않는다. 여기까지는 직관적이다.
문제는 같은 트랜잭션 안에서 같은 쿼리를 두 번 실행하면 결과가 다를 수 있다는 것이다. 그 사이 다른 트랜잭션이 값을 바꾸고 커밋했다면 두 번째 조회에 반영된다. 이걸 non-repeatable read라고 부른다.
잔액을 읽고 계산한 뒤 다시 읽어 검증하는 코드는 이 수준에서 깨진다.
Repeatable Read
트랜잭션이 시작한 시점의 스냅샷을 계속 본다. 다른 트랜잭션이 무엇을 커밋하든 내가 보는 값은 변하지 않는다.
MySQL InnoDB와 PostgreSQL 모두 MVCC로 구현한다. 값을 덮어쓰는 대신 버전을 남기고, 각 트랜잭션은 자기 시점에 맞는 버전을 읽는다. 그래서 읽기가 쓰기를 막지 않는다.
팬텀 리드
범위 조회에 새로 삽입된 행이 끼어드는 현상이다. 표준적으로는 Repeatable Read에서도 허용되지만, InnoDB는 갭 락으로 조회한 범위 자체를 잠가 상당 부분 막는다. 같은 이름의 격리 수준이 DB마다 다르게 동작한다는 뜻이다.
실무에서 확인할 것
- 기본값을 안다. PostgreSQL은 Read Committed, MySQL InnoDB는 Repeatable Read다. 같은 코드가 DB를 바꾸면 다르게 동작할 수 있다
- 읽고 판단해서 쓰는 구간을 찾는다. 이 패턴이 격리 수준에 가장 민감하다
- 그런 구간은 격리 수준을 올리기보다
SELECT ... FOR UPDATE나 조건부 갱신으로 의도를 코드에 드러내는 편이 안전하다
정리
격리 수준은 성능 손잡이가 아니라 정확성 계약이다. 올리기 전에 “지금 무엇이 깨지고 있는가”를 먼저 특정해야 한다.