Update
Delete
했을 때 발생)Insert
했을 때 발생)Update
Delete
했을 때 발생)Insert
했을 때 발생)사용자가 글을 조회할 때, 다른 사용자가 댓글을 다는 중이라도 최신 상태를 확인할 수 있어야 함
Insert
했을 때 발생)내가 궁금했던 내용은 REPEATABLE READ
수준에서는 어떤한 이유로 UPDATE에 대해서는 데이터의 정합성이 보장되고, 또 INSERT에 대해서는 정합성이 보장이 안되는지 궁금했다.
기본적으로 REPEATABLE READ
격리 수준에서 MVCC(Multi-Version Concurrency Control) 방식으로 트랜잭션이 시작될 때 특정 시점의 데이터를 읽도록 하기 때문이다.
✅ MVCC가 동작하는 방식
1. 트랜잭션이 시작될 때, 그 시점에 undo 로그 기반으로 커밋된 데이터의 버전 스냅샷을 생성.
2. 이후 트랜잭션이 계속 진행되는 동안 이 스냅샷을 유지.
3. 다른 트랜잭션에서UPDATE
나DELETE
를 수행하고COMMIT
하더라도, 현재 트랜잭션에서는 초기 스냅샷의 데이터를 계속 유지하여 동일한 결과를 보장함.
✅ 그럼 왜 INSERT된 데이터(팬텀 레코드)는 MVCC로 차단되지 않는가?
- MVCC는 기존 행을 추적할 뿐, 새로운 행을 관리하지 않음
REPEATABLE READ
는 트랜잭션이 시작될 때 현재 존재하는 데이터의 특정 버전을 유지함.- 하지만, 새로운 행(
INSERT
)이 추가될 경우, 이는 기존 버전 관리 대상이 아니므로 MVCC 스냅샷을 통해 차단할 수 없음.- 즉, INSERT된 데이터는 기존 스냅샷과 무관하게 물리적인 디스크 또는 인덱스에서 조회될 수 있음.
Gap Lock
사용으로 Phantom Read 방지SELECT ... FOR UPDATE
또는 SELECT ... LOCK IN SHARE MODE
를 사용하여 해당 범위 내에서 새로운 INSERT
가 차단되도록 함.
Gap Lock은 기존 행과 행 사이의 "공백(Gap)"에도 락을 걸어 새로운 행이 추가되지 못하도록 하는 방식임.
단점: GAP Lock은 데드락이 걸릴 수 있음
데드락 예시
✅ Deadlock 발생 예제
-- 트랜잭션 1 (A)
BEGIN;
SELECT * FROM employees WHERE salary BETWEEN 3000 AND 5000 FOR UPDATE;
sql
복사
편집
-- 트랜잭션 2 (B)
BEGIN;
SELECT * FROM employees WHERE salary BETWEEN 4000 AND 6000 FOR UPDATE;
-- 🚨 트랜잭션 1이 완료될 때까지 대기
은행 송금 시스템
이때 내가 궁금한 점은 SERIALIZABLE
에서는 모든 트랜잭션이 직렬적으로 실행된다면, "왜 Shared Lock이 필요할까?" 라는 것이다
SERIALIZABLE
격리 수준이 "모든 트랜잭션을 직렬적으로 실행"하는 것과 같다고 오해하기 쉽지만,실제로는 DBMS가 직렬화된 순서를 보장하는 방식이지, 물리적으로 한 트랜잭션씩 실행하는 것은 아니다
Shared Lock을 적용하여, 다른 트랜잭션에서 INSERT
, UPDATE
, DELETE
가 차단되므로 직렬화 효과가 발생하는 것이다
항공사 좌석 예약 시스템
- 동시성 : 동시에 수행하는 트랜잭션 양
- 일관성 : 트랜잭션의 작업 처리 결과가 항상 일관성이 있어야 한다는 것