동시 실행되는 트랜잭션이 서로의 작업을 얼마나 볼 수 있는지 정하는 단계. 격리를 강하게 할수록 안전하지만 동시성이 떨어진다.
네 단계와 이상현상
| 수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | 발생 | 발생 | 발생 |
| READ COMMITTED | 차단 | 발생 | 발생 |
| REPEATABLE READ | 차단 | 차단 | 발생(이론상) |
| SERIALIZABLE | 차단 | 차단 | 차단 |
세 이상현상의 차이
Dirty Read : 커밋 안 된 값을 읽음 (롤백되면 유령 데이터)
Non-Repeatable Read : 같은 행을 두 번 읽으니 값이 다름 (UPDATE 때문)
Phantom Read : 같은 조건으로 두 번 읽으니 행 수가 다름 (INSERT 때문)
앞의 둘은 행 단위 문제이고, 팬텀은 범위 문제다. 행 락으로는 팬텀을 막을 수 없다 — 없는 행을 미리 잠글 수 없기 때문이다.
DB별 기본값 — 자주 묻는다
| DB | 기본 격리수준 |
|---|---|
| MySQL(InnoDB) | REPEATABLE READ |
| PostgreSQL · Oracle · SQL Server | READ COMMITTED |
MySQL만 다르다. 역사적으로 복제(statement 기반 binlog)의 정합성 때문에 높게 잡은 것이 이어졌다.
MySQL의 REPEATABLE READ는 팬텀도 거의 막는다
InnoDB는 넥스트 키 락(레코드 락 + 갭 락)으로 행 사이의 빈 공간까지 잠근다.
인덱스: … 10 ── (갭) ── 20 ── (갭) ── 30 …
↑
WHERE age > 15 조건이면 이 갭도 잠가
age=17 을 새로 INSERT 하려는 트랜잭션을 막는다
그래서 이론상 허용되는 팬텀이 실제로는 잘 안 생긴다. 대가는 데드락 증가다 — 잠그는 범위가 넓어지니 충돌이 잦아진다.
무엇을 고르나
대부분 기본값을 그대로 쓰고, 정합성이 중요한 특정 구간만 다르게 다룬다.
- 격리수준을 올리는 것보다
- 낙관적 락(버전 컬럼)
- 조건부 UPDATE (WHERE status = 'PENDING')
- 유니크 제약
- 쪽이 범위가 좁아 성능에 유리하다
격리수준은 트랜잭션 전체에 걸리지만, 위 방법들은 필요한 행에만 걸리기 때문이다.