Lock
여러 Transaction이 같은 데이터에 read / write를 한다면 예기치 못한 데이터 변경이 일어날 수 있다.
이를 막기 위해 데이터를 Lock이라는 장치를 설정하여, Lock을 취득하기 전까진 그 데이터의 read/write를 못하게 막을 수 있다.
Write Lock (Exclusive Lock)
- read / write(insert, modify, delete)할 때 사용한다.
- 다른 transaction이 같은 데이터를 read / write 하는 것을 허용하지 않는다.
- write 할 때만 사용하는 것은 아니다.
Read Lock (Shared Lock)
- read 할 때 사용한다.
- 다른 transaction 같은 데이터를 read 하는 것은 허용한다.
Lock을 사용한 시나리오
상황
- Transaction 1과 Transaction 2이 있다.
- DB에 X = 10이라는 데이터가 있다.
write_lock + read_lock
- Transaction 2는 read_lock(x)를 얻는다.
- Transaction 1은 write_lock(x)를 획득하려 한다. 하지만 read_lock(x)이 끝날 때까지 기다려야 한다.
- Transaction 2는 read(x) = 10을 수행하고, unlock(x) 한다.
- Transaction 1은 write_lock(x)를 얻고 write(x)를 수행한다.
- write(x) 후 unlock(x) 한다.
read_lock + read_lock
- Transaction 2는 read_lock(x)를 얻는다.
- Transaction 1은 read_lock(x)를 얻는다.
- Transaction 1, 2는 read(x) = 10 을 동시에 수행하고, unlock 한다.
Lock을 사용해도 문제가 생기는 경우
상황
- Transaction 1과 Transaction 2이 있다.
- DB에 x = 100, y = 200이라는 데이터가 있다.
- Transaction 1은 x와 y의 합을 x에 저장하려 한다.
- Transaction 2는 x와 y의 합을 y에 저장하려 한다.
- Transaction 2가 먼저 수행하고, 1이 수행했을 때 결과는 x = 400, y = 300이다.
시나리오
- Transaction 2가 read_lock(x)를 수행한다.
- Transaction 2가 read(x) = 100을 수행하고 unlock(x)를 한다.
- Transaction 1은 read_lock(y)를 수행한다.
- Transaction 2는 write_lock(y)를 수행한다. 하지만 Transaction 1이 read_lock(y)를 갖고 있으므로 block 된다.
- Transaction 1은 read(y) = 200을 수행하고 unlock(y)를 한다.
- Transaction 2는 read(y) = 200을 수행한다.
- Transaction 2는 read(y) = 200을 수행하고 unlock(y)를 한다.
- Transaction 1은 write_lock(x)를 수행하고, read(x) = 100 -> write(x = 300) -> unlock(x)를 수행한다.
- 결과적으로 DB에 반영된 데이터는 x = 300, y = 300이다.
이 문제는, Transaction 2가 write를 하기 전에 read lock을 반납하여, Transaction 1이 Transaction 2가 데이터를 업데이트하기 전 데이터 y를 read_lock을 수행하여 읽었기 때문이다.
2PL Protocol (two-phase locking)
Transaction에서 모든 Locking Operation이 최초의 unLock Operation보다 먼저 수행되도록 하는 것이다.
각 phase는 두 가지 종류가 있다.
Expanding phase (growing phase)
lock을 취득하기만 하고 반환하지는 않는 phase
Shrinking phase (contracting phase)
lock을 반환만 하고 취득하지는 않는 phase
2PL을 사용한 위 이상한 문제 해결
상황
- Transaction 1과 Transaction 2이 있다.
- DB에 x = 100, y = 200이라는 데이터가 있다.
- Transaction 1은 x와 y의 합을 x에 저장하려 한다.
- Transaction 2는 x와 y의 합을 y에 저장하려 한다.
- Transaction 2가 먼저 수행하고, 1이 수행했을 때 결과는 x = 400, y = 300이다.
시나리오
- Transaction 2가 read_lock(x)를 수행한다.
- Transaction 2가 read(x) = 100을 수행하고 write_lock(y)를 한다.
- Transaction 1은 read_lock(y)를 수행하려 하지만, Transaction 2가 write_lock를 수행하고 있으므로 block된다.
- Transaction 2는 unlock(x)를 수행한다.
- Transaction 2는 read(y) = 200을 수행하고 write(y = 300)을 수행한다.
- Transaction 2는 unlock(y)를 한다.
- Transaction 1은 Transaction 2가 write한 값인 y값을 읽기 위해 read(y) = 300을 수행한다.
- Transaction 1은 read(x) = 100을 수행하고 write(x = 400)을 수행한다.
- Transaction 1은 unlock(x)를 한다.
- 결과적으로 DB에 반영된 데이터는 x = 400, y = 300이다.
위 1 ~ 2번 과정이 Transaction 2의 Expanding phase이고, 4 ~ 6이 Shrinking phase로 나눌 수 있다.
2PL과 DeadLock
하지만 2PL을 사용한 Transaction에는 DeadLock이 발생할 수 있다.
상황
- Transaction 1과 Transaction 2이 있다.
- DB에 x = 100, y = 200이라는 데이터가 있다.
- Transaction 1은 x와 y의 합을 x에 저장하려 한다.
- Transaction 2는 x와 y의 합을 y에 저장하려 한다.
시나리오
- Transaction 2가 read_lock(x)을 수행한다.
- Transaction 1은 read_lock(y)을 수행한다.
- Transaction 1은 read(y) = 200 을 수행하고 write_lock(x)를 수행하려 한다.
- 하지만 Transaction 2가 read_lock(x)를 갖고 있기 때문에, Transaction 1의 write_lock(x)는 block된다.
- Transaction 2는 read(x)를 수행하고 write_lock(y)를 수행하려 한다.
- 하지만 Transaction 1이 write_lock(x)를 수행하기 위해 대기하고 있어, Transaction 2의 write_lock(y)를 기다리게 된다.
- 결국 서로가 Lock을 얻기 위해 기다리는 DeadLock 상태에 들어가게 된다.
2PL의 종류
Conservative 2PL
- 모든 Lock을 취득한 뒤 transaction을 시작하는 방법이다.
- 즉, 연산에 필요한 read_lcok, write_lock을 모두 쥐고나서 Transaction을 시작한다.
- 이러한 방법은 DeadLock이 발생하지 않는다.
- 하지만 실용적이지 못하다.
Strict 2PL(S2PL)
- Strict Schedule을 보장하는 2PL 방법이다. (Strict Schedule: 스케줄 내에서 어떤 transaction도 commit 되지 않은 transaction들이 write한 데이터는 읽지도 않고 쓰지도 않는 스케줄)
- Recoverability를 보장한다. (Recoverability: Rollback을 했을 때 문제가 발생하지 않는 속성)
- Write-Lock을 commit / rollback 될 때 반환한다.
Strong Strict 2PL(SS2PL)
- Strict Schedule을 보장하는 2PL 방법이다.
- Recoverability를 보장한다. (Rollback을 했을 때
- Read-Lock과 Write-Lock 모두 commit / rollback 될 때 반환한다.
- S2PL 보다 구현이 쉽다.
- 대신, Lock을 오래 쥐고 있어 다른 Transaction이 Lock을 필요로 할 때 오래 기다리게 된다.
Lock 호환성 문제와 MVCC (multiversion concurrency control)
아래 표처럼, read-lock 끼리는 동시에 Transaction이 진행될 수 있지만, 그 외에 상황에선 동시에 진행할 수 없다.
따라서 read-lock <-> write-lock 끼리라도 동시에 진행 가능하도록 나온 방법이 MVCC이다.
read-lock | write-lock | |
read-lock | O | X |
write-lock | X | X |
참고
'ComputerScience > Database' 카테고리의 다른 글
[Database] DBCP (DB connection pool)과 hikariCP, MySQL을 기준으로 (0) | 2024.07.13 |
---|---|
[Database] DB MVCC와 PostgreSQL, MySQL의 동작 비교 (0) | 2024.04.27 |
[Database] DB concurrency control: schedule과 serializability, recoverability (0) | 2024.04.25 |
[Database] isolation이 안될 때 나타날 수 있는 여러 현상 (0) | 2024.04.21 |
[Database] Join의 의미와 여러 종류의 Join (1) | 2024.03.07 |