데이터베이스 이론 - 동시성 제어(Concurrency Control)
Chapter 18: Concurrency Control (Database System Concepts 7th Edition)을 공부하며 정리한 글입니다.
# 동시성 제어란?
동시에 여러 트랜잭션을 처리할 때 데이터의 일관성을 해치지 않도록 관리하는 방법
(여러 쓰레드가 공유 자원에 접근 시 Race Condition이 발생해서 이를 핸들 해줘야 하는 것과 동일)
비슷한 맥락으로 DB 역시 Lock을 거는 방법으로 동시성 문제를 해결한다.
# Lock-Based Protocols
DB Lock에는 두 가지 종류가 있다.
배타 잠금(Exclusive Lock) - 읽기/쓰기를 위한 잠금, X-Lock이라고 한다.
공유 잠금(Shared Lock) - 읽기를 위한 잠금, S-Lock이라고 한다.
트랜잭션은 데이터에 접근할 때 Concurrency Control Manager를 통해 Lock 할당 요청을 하고, 이를 할당받아서 진행한다.
X-Lock을 거는 연산을 lock-X, S-Lock을 거는 연산은 lock-S라고 명시한다.
잠금을 가지고 있는 트랜잭션이 서로 S-Lock이면 (즉, S-Lock - S-Lock) 데이터를 대기 없이 읽을 수 있다.
다른 경우는 모두 안 된다. 이를 잠금 호환성(Lock-compatibility)라고 한다.
Lock-Based Protocol은 Deadlock과 Starvation이라는 고질적인 문제를 동반한다.
# 교착상태 (Deadlock)
잠금을 이용하면 동시성 문제를 해결하지만, 교착상태라는 다른 문제가 발생할 수 있다.
교착상태란 잠금을 대기하던 두 트랜잭션이 꼬여서 서로 무한정 대기하게 되는 상태를 말한다.
이럴 경우 롤백을 진행해야 한다.
Deadlock vs. Consistency
DB입장에선 무슨 일이 있어도 일관성을 보장해줘야 한다. 일관성을 해치는 건 핸들 할 수 없지만, 데드락은 롤백을 통해 핸들 가능하기 때문에 차라리 데드락을 발생시키는 프로토콜을 가져가는 게 맞다.
# Locking Protocol
모든 트랜잭션이 잠금 요청(requesting)과 해제(releasing)를 일관되게 따르는 규칙의 집합을 말한다.
스케줄 S가 legal(합법적)이다는 뜻은 해당 LP를 잘 따르고 있다는 뜻이다.
LP가 충돌 직렬 가능성(conflict serializability)을 보장(ensure)한다는 뜻은, legal 스케줄들이 모두 충돌 직렬 가능하다는 뜻이다.
# 기아문제 (Starvation)
다음 상황을 가정해 보자.
1. T2가 S-Lock을 데이터 Q에 걸었다.
2. T1이 X-Lock을 Q에 요청했다.
3. T1은 T2가 S-Lock을 해제할 때까지 기다린다. (호환성이 없으므로)
4. 그 와중, T3이 Q에 S-Lock을 요청한다. T2와 T3은 S-Lock 호환성이 있어서 T3은 접근 가능하다.
5. 그 와중, T2가 락을 놓는다. 하지만 T1은 T3이 해제할 때 까지 기다린다.
6. 다시 새로운 트랜잭션 T4가 S-Lock을 Q에 요청한다.
...
이러면 T1은 Q에 대해 X-Lock을 계속 얻지 못하게 된다!!
즉, S-Lock과 X-Lock의 호환성 문제 때문에 기아문제가 발생 가능해진다.
S-Lock을 먼저 걸고 그 뒤 트랜잭션이 X-Lock을 걸려고 할 때, 다른 트랜잭션들이 S-Lock을 들고 계속 접근하면 X-Lock을 요청한 트랜잭션은 계속해서 기다리게 된다. (자원을 선점할 수 없게 된다)
# Two-Phase Locking Protocol (2PL)
필요한 잠금을 쭉 먼저 다 걸고, 나중에 쭉 해제하는 방식
Growing phase : 모든 트랜잭션은 잠금을 걸 수만 있다.
Shirinking phase : 모든 트랜잭션은 잠금을 해제만 할 수 있다.
1. 최초로 트랜잭션은 Growing phase에 놓인다.
2. 트랜잭션은 필요한 잠금들을 획득한다.
3. 트랜잭션이 최초로 잠금을 해제하면, 그 순간부터 Shirinking phase가 시작된다.
장점 : 충돌 직렬 가능 스케줄들을 보장한다.
단점 : 교착상태가 발생할 수 있다; Cascading Rollback이 발생할 수 있다.
Strict 2PL
X-Lock을 커밋 전까지 해제하지 않는다. -> Cascading Rollback을 방지한다.
Rigorous 2PL
모든 잠금을 커민 전 까지 해제하지 않는다.
(구현이 쉬워서 대부분 DB는 이를 구현한다.)
# Lock Conversion
2PL에서 직렬 가능성을 보장하기 위해 잠금을 변경하는 행위를 한다.
Growing phase
S-Lock과 X-Lock을 둘 다 잡을 수 있으면, 해당 잠금을 X-Lock으로 업그레이드한다.
Shirinking phase
S-Lock과 X-Lock을 둘 다 해제할 수 있으면, 해당 잠금을 S-Lock으로 다운그레이드 한다.
# Locking의 구현
위 과정들을 실제로 구현한 게 Lock Manager다.
트랜잭션은 잠금 요청과 해제 요청을 메시지 형태로 Lock Manager에게 전송한다.
Lock Manger는 해당 잠금 요청을 처리하고 권한(Grant) 메시지를 다시 보낸다.
Lock Manager는 lock table이라는 인메모리 자료구조를 사용해서 레코드들의 잠금 상태를 관리한다.
Lock-Based Protocol은 잠금을 걸면서 동시성을 제어하지만, 교착상태/기아문제 등 문제를 발생시킬 수 있고, 잠금을 걸 기 때문에 이 과정에서 속도상으로 조금 느려질 수 있다. 그래서 잠금을 걸지 않고 동시성을 제어하는 방법도 존재한다.
# Snapshot Isolation
트랜잭션이 수행될 때 DB 현재 상태를 마치 사진 찍은 것 마냥 스냅샷으로 가져와서 수행하고 커밋하면 반영한다.
이렇게 해서 동시적으로 트랜잭션을 수행할 때 잠금을 걸지 않도록 할 수 있다.
First committer wins : 가장 빠르게 커밋한 트랜잭션의 내용이 DB에 반영된다.
Snapshot Read : 동시적인(Concurrent) 업데이트는 스냅샷 읽기에서 보이지 않는다.
장점
- 읽기 작업이 블로킹되지 않는다.
- Read Committed와 비슷한 성능을 보인다.
- Dirty Read 없음, Lost update 없음, Non-repeatable read 없음
단점
- Serializable 한 수행을 항상 보장하지 않는다. (Skew Write/Insert 문제)
Serializable Snapshot Isolation (SSL)
write-write 충돌을 추적한다. (read-write 충돌은 추적하지 않음)
이를 추적해서 진행 도중 cycle이 발생하면, 롤백한다.