카테고리 없음

(데이터베이스) Repeatable Read에서 Write Skew가 발생하는 이유와 DBMS별 대응 전략 (예제 포함)

불광동 물주먹 2025. 5. 23. 00:00

Repeatable Read에서 Write Skew가 발생하는 이유와 DBMS별 대응 전략

 

트랜잭션의 일관성을 보장하기 위한 격리 수준 중 하나인 `Repeatable Read`는 이름만 보면 마치 모든 데이터를 고정된 스냅샷으로 보장해줄 것처럼 보입니다. 하지만 실제로는 **Write Skew**와 같은 정합성 오류가 발생할 수 있는 여지가 있습니다.

이 글에서는 `Repeatable Read` 격리 수준에서 발생할 수 있는 Write Skew 현상을 설명하고, 이를 방지하기 위한 전략으로 **Locking Read (SELECT ... FOR UPDATE)** 나 **Serializable 격리 수준**을 사용하는 이유를 정리합니다. 또한, **Oracle, MySQL(InnoDB), PostgreSQL**에서 이 문제를 각각 어떻게 처리하는지도 비교 분석해 실무에서의 선택 기준을 제공합니다.

 

 

🔸 Skew Write (Write Skew)

Write Skew는 Repeatable Read에서도 발생할 수 있는 anomaly로, 같은 조건을 기준으로 여러 트랜잭션이 동시에 업데이트할 때 생김.

예시:

-- 조건: 두 명 이상이 duty에 없어야 함

-- Tx1
SELECT COUNT(*) FROM employees WHERE on_duty = false;  -- 결과 1명
-- (조건 만족)
UPDATE employees SET on_duty = true WHERE id = 1;

-- Tx2 (동시에)
SELECT COUNT(*) FROM employees WHERE on_duty = false;  -- 결과 1명
UPDATE employees SET on_duty = true WHERE id = 2;

 

결과적으로 2명이 동시에 on_duty가 true가 되어버리는 문제.


🔸 DB별 Skew Write 처리 차이


 

DBMS
Repeatable Read의 Skew Write 허용 여부 SERIALIZABLE 동작 방식
Oracle 허용 안 함 (Skew Write 방지됨) Serializable 없어도 강력한 정합성 보장
MySQL 허용함 (InnoDB에서는 write skew 발생 가능) MVCC를 무시하고 table-level lock 비슷하게 동작
PostgreSQL 허용함 (하지만 충돌 감지 후 첫 커밋만 성공) Serializable Snapshot Isolation + 충돌 시 자동 롤백
 

🔸 Oracle의 특이점

  • Oracle은 REPEATABLE READ가 존재하지 않음
    • 대신 READ COMMITTED (기본값) 와 SERIALIZABLE 제공.
    • READ COMMITTED인데도 Write Skew가 방지됨
      • 이유: Consistent read는 undo 기반이고, DML 시점에 row-level lock을 강제하기 때문에.
  • 즉, 오라클에서는 SELECT FOR UPDATE 없이도 write skew를 방지하는 경우가 많음

🔸 For Update를 Read 시점에 쓰는 케이스?

오라클은 MVCC가 undo 기반이라, 단순 SELECT는 과거 스냅샷 읽고 끝남.
따라서 읽은 후에 반드시 수정이 필요한 경우에만 FOR UPDATE를 붙여서 락을 걸어줌.

반면:

  • MySQL / PostgreSQL에서는 REPEATABLE READ라도 skew write가 발생할 수 있기 때문에,
  • 비즈니스 상 조건 판단 후 업데이트가 있는 경우에는 선제적으로 SELECT ... FOR UPDATE를 추천함.

🔸 MySQL Serializable은 Lock처럼 동작한다?

거의 맞습니다.

  • InnoDB에서 SERIALIZABLE은 다음을 강제:
    • 모든 SELECT에 암묵적으로 LOCK IN SHARE MODE(= SELECT ... FOR SHARE) 가 적용됨.
    • 실제로는 table lock처럼 동작하여 성능이 떨어짐.
    • 진짜 MVCC라기보다는 lock-based concurrency처럼 작동.

🔸 PostgreSQL Serializable

  • PostgreSQL의 SERIALIZABLE은 완전한 MVCC를 기반으로 하며, 이름은 같지만 작동 방식은 Oracle/MySQL과 다름.
  • SSI (Serializable Snapshot Isolation) + 첫 커밋 성공 정책 (First-committer-wins)
  • 충돌이 감지되면 나중 트랜잭션은 강제 롤백

🔸 결론 요약

  • Oracle은 FOR UPDATE가 읽은 후 반드시 변경하는 로직에만 사용되고,
    MVCC가 undo 기반이므로 READ COMMITTED 만으로도 대부분의 anomaly 방지 가능.
  • MySQL/PostgreSQL은 Repeatable Read에서도 Skew Write 방지 못하므로,
    반드시 SELECT ... FOR UPDATE 또는 SERIALIZABLE 레벨 설정이 필요.
  • PostgreSQL은 MVCC + SSI로 SERIALIZABLE을 성능 저하 없이도 제공.