아직 커밋되지 않은 다른 트랜잭션의 변경을 읽어 버리는 이상현상.
어떻게 문제가 되는가
T1: UPDATE account SET balance = 0 WHERE id = 1; -- 원래 1000, 아직 커밋 안 함
T2: SELECT balance FROM account WHERE id = 1; -- 0 을 읽음
T2: if (balance == 0) 계정정지(); -- 정지 처리 실행
T1: ROLLBACK; -- 되돌림 → 잔액은 다시 1000
존재한 적 없는 값을 근거로 판단해 버렸다. T1이 롤백했으므로 잔액 0인 순간은 DB 역사에 없었는데, T2는 그것을 보고 행동했다.
왜 "dirty"인가
커밋 전 데이터는 언제 사라질지 모르는 더러운 값이라는 뜻이다. 확정되지 않았으므로 신뢰할 수 없다.
차단 방법
READ COMMITTED 이상이면 발생하지 않는다. 이름 그대로 "커밋된 것만 읽는다"는 뜻이다.
- 락 기반 구현이라면 쓰기 락이 걸린 행은 읽지 못하게 막는다
- MVCC 구현이라면 커밋 전 버전은 Read View에서 보이지 않는다
MySQL·PostgreSQL 모두 기본 격리수준이 READ COMMITTED 이상이므로 실무에서 Dirty Read를 만날 일은 거의 없다.
그럼 READ UNCOMMITTED는 왜 있나
정확성이 전혀 중요하지 않고 대략적인 값만 빠르게 보면 되는 경우에 쓴다. 예를 들어 관리자 화면의 "현재 처리 중 건수" 같은 대시보드. 락을 전혀 안 걸어 가장 빠르다.
다만 요즘은 MVCC 덕분에 READ COMMITTED도 읽기에 락을 걸지 않으므로, READ UNCOMMITTED를 쓸 이유가 사실상 없다.
면접에서
Dirty Read 자체보다 "세 이상현상을 구분할 수 있는가" 가 핵심이다.
| 무엇이 문제 | 원인 연산 | |
|---|---|---|
| Dirty Read | 커밋 안 된 값을 봄 | 아무 것 |
| Non-Repeatable Read | 같은 행의 값이 바뀜 | UPDATE |
| Phantom Read | 행의 개수가 바뀜 | INSERT·DELETE |