데이터베이스 용어 사전
트랜잭션오손 읽기 · Non-repeatable Read · 반복 불가 읽기

Dirty Read

커밋되지 않은 남의 변경을 읽는 현상. 그 트랜잭션이 롤백되면 존재한 적 없는 값을 본 셈이 된다.

다른 트랜잭션이 아직 커밋하지 않은 변경을 읽어 버리는 현상.

무엇이 문제인가

T1: UPDATE balance = 0        (아직 커밋 안 함)
T2: SELECT balance  → 0       ← 커밋 안 된 값을 읽었다
T1: ROLLBACK                  ← 사실 0이 아니었다

T2 는 '존재한 적 없는 값'을 보고 판단을 내린 셈이다

이 판단으로 메일을 보냈거나 다른 테이블을 갱신했다면 되돌릴 방법이 없다.

형제 현상 — Non-repeatable Read

T1: SELECT balance FROM account WHERE id='A'  → 10000
T2: UPDATE account SET balance=5000 WHERE id='A'; COMMIT;
T1: SELECT balance FROM account WHERE id='A'  → 5000   ← 같은 행인데 값이 달라졌다

같은 행을 두 번 읽었는데 값이 다른 현상이다. Dirty Read와 달리 커밋된 값을 읽었는데도 문제가 된다 — 한 트랜잭션 안에서 같은 질문에 다른 답이 나오면 합계와 상세가 어긋나는 식의 모순이 생긴다.

어디까지 막히나

  • READ UNCOMMITTED — Dirty Read 가능
  • READ COMMITTED — Dirty Read 막음 · Non-repeatable Read 가능
  • REPEATABLE READ — 둘 다 막음

PostgreSQL은 READ UNCOMMITTED를 요청해도 READ COMMITTED로 동작한다 — MVCC 구조상 커밋 안 된 버전을 보여줄 방법 자체가 없기 때문이다. 즉 PostgreSQL에서 Dirty Read는 애초에 일어나지 않는다.

왜 MVCC가 이걸 공짜로 푸나

각 트랜잭션은 '자기 시점의 스냅샷'을 본다

  • 커밋되지 않은 버전은 애초에 스냅샷에 포함되지 않는다
  • 읽기가 쓰기를 기다릴 필요도 없다

락 기반(2PL)에서는 읽기가 쓰기 락을 기다려야 막을 수 있었던 문제를, MVCC는 기다림 없이 해결한다.

면접 함정

  • "Dirty Read와 Non-repeatable Read는 같은 말" → 전자는 커밋 안 된 값, 후자는 커밋된 값이 바뀐 것이다.
  • "READ UNCOMMITTED가 제일 빠르니 조회에 쓰자" → PostgreSQL에서는 효과가 없고, 다른 엔진에서도 얻는 성능 대비 위험이 크다.

PostgreSQL에서는 왜 아예 없나

BEGIN ISOLATION LEVEL READ UNCOMMITTED;
SHOW transaction_isolation;    -- read committed   ← 요청해도 승격된다

MVCC 구조상 커밋되지 않은 버전은 어떤 스냅샷에도 포함되지 않는다. 보여줄 방법 자체가 없어서 READ UNCOMMITTED를 구현할 필요가 없었다. "PostgreSQL은 dirty read가 없다"는 관용구가 여기서 나온다.

MySQL에서 재현해 보기

-- [세션 1]
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT balance FROM account WHERE id='A';   -- 세션 2가 커밋 안 한 값이 보인다

-- [세션 2]
BEGIN;
UPDATE account SET balance = 0 WHERE id='A';   -- 커밋하지 않는다

Non-repeatable Read가 실제로 아픈 곳

-- 리포트 하나가 여러 번 조회할 때
BEGIN;
SELECT sum(amount) FROM orders WHERE status='PAID';   -- 합계 1,000,000
-- ... 그 사이 누가 주문을 취소하고 커밋 ...
SELECT count(*)   FROM orders WHERE status='PAID';    -- 건수는 새 값
COMMIT;
-- 합계와 건수가 서로 다른 시점의 데이터 → 리포트가 앞뒤가 안 맞는다

합계와 상세가 어긋나는 리포트가 이 현상의 전형적 피해다. 한 트랜잭션 안에서 여러 번 조회한다면 REPEATABLE READ가 필요하다는 신호다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 트랜잭션 — ACID·격리수준·이상현상(PostgreSQL·MySQL)