네 가지 격리수준, SQL 표준과 실제 구현은 왜 다른가
표준은 최소 기준일 뿐이다
SQL 표준(SQL-92)은 네 가지 격리수준을 "허용되는 이상현상의 최대치"로 정의합니다. Read Uncommitted는 다 허용, Read Committed는 Dirty Read만 막음, Repeatable Read는 거기에 더해 값 변경까지 막음, Serializable은 전부 막음. 이 정의를 그대로 믿고 DB마다 똑같이 동작한다고 생각하면 실무에서 반드시 한 번은 당황하게 됩니다. 표준은 "이보다 더 느슨하면 안 된다"는 하한선이지, "정확히 이렇게 구현하라"는 명세가 아니기 때문입니다.
-- 세션 설정 방법은 어느 DB든 비슷하다
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ... 쿼리들 ...
COMMIT;PostgreSQL — 세 단계만 실제로 존재한다
PostgreSQL은 네 가지 이름을 모두 받아주지만 내부적으로는 세 가지만 존재합니다.
READ UNCOMMITTED를 설정해도 조용히 READ COMMITTED로 동작합니다. 커밋되지 않은
데이터를 읽을 방법 자체가 없기 때문입니다. 그리고 앞 장에서 본 것처럼, PostgreSQL의
REPEATABLE READ는 표준보다 엄격해서 Phantom Read까지 막습니다 — Snapshot Isolation을
그대로 구현했기 때문입니다.
| PostgreSQL 설정 이름 | 실제 동작 |
|---|---|
| READ UNCOMMITTED | READ COMMITTED와 동일 |
| READ COMMITTED (기본값) | 문장 단위 스냅샷 |
| REPEATABLE READ | 트랜잭션 시작 시점의 스냅샷 고정, Phantom도 차단 |
| SERIALIZABLE | SSI(직렬화 가능 스냅샷 격리), 5장·8장에서 다룸 |
MySQL InnoDB — 기본값부터 다르다
여기서 실무 혼선이 가장 자주 생깁니다. MySQL InnoDB의 기본 격리수준은 Repeatable Read입니다. 대부분의 다른 RDBMS(PostgreSQL, Oracle, SQL Server)는 Read Committed를 기본값으로 씁니다. 마이그레이션이나 멀티 DB 환경에서 이 차이를 모르면, 같은 애플리케이션 코드가 DB만 바꿨을 뿐인데 다른 동시성 버그를 보이거나 반대로 사라지는 일이 생깁니다.
InnoDB의 Repeatable Read는 PostgreSQL과 마찬가지로 Phantom Read를 막지만, 방법이 다릅니다. PostgreSQL은 순수하게 MVCC 스냅샷으로 막는 반면, InnoDB는 갭 락(gap lock)이라는 락 메커니즘을 추가로 씁니다(6장에서 자세히 다룹니다). 이 차이는 데드락이 발생하는 패턴에도 그대로 영향을 미칩니다.
Oracle과 SQL Server — 또 다른 갈래
Oracle은 표준의 REPEATABLE READ를 아예 지원하지 않고, 대신 READ COMMITTED와
SERIALIZABLE만 제공합니다. 그리고 Oracle의 SERIALIZABLE은 사실 진짜 직렬 가능성을
전부 보장하지 않고 Snapshot Isolation에 가깝게 동작합니다 — 이름과 실제 동작이 다시
어긋나는 지점입니다. SQL Server는 표준 네 단계에 SNAPSHOT이라는 다섯 번째 옵션을
추가로 제공해서, 락 기반 Repeatable Read와 MVCC 기반 Snapshot Isolation을 사용자가
직접 고르게 합니다.
flowchart TD
A["격리수준을 정할 때"] --> B{"어떤 DB를 쓰는가?"}
B -->|PostgreSQL| C["기본 Read Committed\nRepeatable Read = Phantom 차단"]
B -->|MySQL InnoDB| D["기본 Repeatable Read\n갭 락으로 Phantom 차단"]
B -->|Oracle| E["Repeatable Read 없음\nSerializable도 사실상 Snapshot"]
B -->|SQL Server| F["기본 Read Committed\nSNAPSHOT 옵션 별도 존재"]이 장이 말하고 싶은 것
같은 이름(REPEATABLE READ)을 설정해도 어떤 DB인지에 따라 실제로 막히는 이상현상과
그 구현 방식이 다릅니다. 그래서 "우리는 Repeatable Read를 쓰니까 안전하다"는 문장은
그 자체로는 아무 의미가 없습니다 — 어떤 DB의 Repeatable Read인지를 알아야 비로소
의미가 생깁니다. 이 책의 나머지 절반은 이 "실제 동작"을 만드는 두 가지 메커니즘,
MVCC(5장)와 락(6장)을 파헤치는 데 씁니다.