MySQL, Oracle의 Locking 기법과는 좀 다른 Snapshot Isolation에 대해 간략하게 엿볼 수 있는 논문이다.
특히 Serializability에 대해 고민해볼 수 있는 좋은 논문이라 생각한다.
나는 Write Skew와 SSI의 구현을 중심적으로 읽고 정리하였다.
더 자세한 내용은 논문을 읽어보길 바란다. 논문이 잘 쓰여서 재밌게 읽을 수 있을 것 같다.
Snapshot Isolation (SI)
- Read가 트랜잭션 시작된 시점의 Committed Version 기준
- 트랜잭션이 시작된 이후의 변경 사항을 볼 수 없음
- 한 트랜잭션 안에서는 일관성 보장
- First-Writer-Wins
- PostgreSQL: MVCC 방식을 사용하여 구현
- Repeatable Read라 불림
장점
- Dirty Reads, Non-Repeatable Reads, Phantom Reads 해결
문제
1. Write Skew
- 두 동시 트랜잭션이 같은 데이터를 읽고, 겹치는 데이터를 수정했을 때 발생
- 하나의 트랜잭션 안에서는 문제X → 결과적으로 논리적인 Constraints 위반
- 예제
- 해결
- 2PL: Select For Update (단점: 성능 하락, Deadlock)
- SI: table 설계를 변경하여 FWW으로 방어 (단점: 설계가 어려움)
2. Batch Processing
- 예제
- T1은 T2의 INSERT를 보지 못함 → 데이터 불일치 발생
- 단순한 이해로는 Write Skew와 비슷한 개념으로 봐도 괜찮을듯 하다.
Serializable Snapshot Isolation (SSI)
- 기존 SI 기반 + 추가 검사 수행
- Serialization Graph 유지 → Cycle 발견 시 Abort
- 추가적인 Blocking이 없다
- Update Conflicts에서는 Abort
- VS 2PL: 충돌할 가능성이 있을 때만 Abort
Dependencies
- Adya et al.이 제안한 Multi-Version Serialization History Graph 상 종속성 유형
- WR dependencies
- W의 commit 후에 R 가능 (직렬 실행 보장)
- WW dependencies
- W1→W2 순서 (직렬 실행 보장)
- RW dependencies
- R2, W1
- T2가 old version을 읽게 됨 → T2가 먼저 실행된 것처럼 보이지만, 실제로는 반대로 실행됐을 수도
- RW-반종속성 (Read-Write Antidependency) (이하 RW라 부름)
⇒ Cycle 발생
- RW가 2개 이상 연결된 형태를 띄면 직렬성 유지X (Adya [1], Fekete et al [10])
- Theorem1) T1→T2, T2→T3 두 개의 RW 존재, T3 먼저 commit
- Corollary2) T1&T2, T2&T3 동시 실행, Write Skew에서는 T1==T3 일 수도 있음
- Cycle 존재 → 직렬성 보장X
- 즉, SSI에서는 RW를 감지하여 직렬성을 보장
핵심
- Cycle을 직접 검사하지 않고, Dangerous Structure를 감지
- 트랜잭션이 하나의 RW를 받아들이면서, 다른 트랜잭션과 RW인 경우(Incoming, Outgoing) → Abort
- Theorem1에 따르면 이로써 직렬성 유지 가능, but 불필요한 abort 가능(False Positive Abort)
- vs. OCC, S2PL: rw를 일부 허용하되, Dangerous Structure일 때만 Abort
False Positive Abort in SSI
- 실제로는 직렬성이 유지될 수 있는 상황이었지만, SSI 알고리즘 상 직렬성이 위반되어 불필요한 Abort
- ex) Data: stock(10), Operation: stock--;
- T1: R1(s:10), W1(s:9) T2: R2(s:10), W2(s:9)
- RW 발생했지만, 직렬 실행 결과와 동일
- 해결 기법
- Commit Ordering Optimization: 모든 트랜잭션이 직렬화 되지는 않아 완벽히 해결X
- Precisely SSI: 전체 Graph를 검사하여 실제로 직렬성이 위반되는 경우에만 Abort
- 메모리 사용량이 크고, 실제 벤치마크 상 Abort 비율이 낮아 이점이 크지 않다고 판단
PostgreSQL 상의 SSI 구현
New Lock - ‘SIREAD’
- PostgreSQL은 기존에 S2PL을 지원X
- SSI는 SI 기반이므로 Postgre에서 쉽게 구현될 것이라 예상하였으나, 실제로 어렵게 되어 결국 S2PL과 유사한 Lock Manager가 필요하게 됨
- MySQL 등은 기존의 Read Lock을 사용할 수 있으나 Postgre는 그럴 수 없다
- 트랜잭션이 데이터를 읽을 때 SIREAD를 설정
- 쓰기 연산 허용(Blocking X, RW 허용) → Deadlock이 발생할 가능성이 없으므로 별도의 Deadlock Detection이 필요하지 않다
- 쓰기 시도 → RW 감지
- RW는 동시 트랜잭션에서 발생 → 모든 트랜잭션이 끝날 때까지 유지 → 커밋한 후에도 Lock 유지 필요
Conflict Detection
- Write가 먼저 발생한 경우
- MVCC로 데이터 Visibility 확인
- 데이터가 트랜잭션의 Snapshot에 보이지 않으면 RW-conflict 발생한 것으로 판단
- old version을 읽은 것이므로 Read가 앞서야 함 → 직렬성이 깨질 가능성 존재
- Read가 먼저 발생한 경우
- MVCC 데이터만으로는 불가능 → SIREAD Lock 사용 필요
Safe Retry
- Abort 후 retry해도 직렬화 보장할 수 있도록. 즉 victim을 누구로 설정할지 결정하는 작업.
- ex) T1→T2→T3 (rw)
- T3 먼저 commit (Therem1)
- 가능하면 T2를 abort
- T3는 이미 커밋되었으므로 abort 불가능
- T2도 커밋되었을 경우 T1을 abort
- 즉, 트랜잭션이 커밋할 때까지 기다렸다가 Abort
- Delayed Abort의 이점
- 트랜잭션의 연산이 무효화되므로 리소스 낭비 발생? → 아닌 경우도 있음
- 이유: 동일한 트랜잭션이 다시 실행되었을 때도 동일한 문제로 Abort될 가능성이 높음
- 즉, Commit Order가 결정된 후 Abort하면 같은 문제가 반복될 가능성이 줄어듦 (최적의 순간에 Abort)
- Delayed Abort의 이점
결국 Locking을 도입하게 되었다는 슬픈 사실...
Write Skew를 풀려고한 노력을 중점적으로 보기에 좋았다.
참고
[논문 대신 읽기] PSQL 에서 Serializable 격리수준을 쓰기 무서우신가요?
- postgreSQL 은 serializable 격리 수준을 lock이 아닌 serializable snapshot isolation(ssi) 을 통해 지원합니다. - 기본이 된 격리수준인 si 를 간단하게 살펴보고, ssi 의 기본 원리와 구현방식을 살펴봅시다.
velog.io
https://dl.acm.org/doi/10.14778/2367502.2367523
Serializable snapshot isolation in PostgreSQL | Proceedings of the VLDB Endowment
This paper describes our experience implementing PostgreSQL's new serializable isolation level. It is based on the recently-developed Serializable Snapshot Isolation (SSI) technique. This is the first implementation of SSI in a production database ...
dl.acm.org