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을 성능 저하 없이도 제공.