상황 | 트랜잭션1 | 트랜잭션2 |
---|---|---|
상황1 | 읽기 | 읽기 |
상황2 | 읽기 | 쓰기 |
상황3 | 쓰기 | 쓰기 |
읽기만 한다면 크게 문제가 없지만 문제는 상황2, 상황3에서 나타난다.
상황2는 Isoltion level에서 다루고 동시성 제어에서는 상황3을 살펴보자
갱신손실(lost update) 문제는 두 개의 트랜잭션이 한 개의 데이터를동시에 갱신할 때 발생한다.
T1 | T2 | 버퍼 값 | Index |
---|---|---|---|
A = read_item(X); A = A - 100 | - | X = 1000 | 1 |
- | B = read_item(X); B = B + 100 | X = 1000 | 2 |
write_item(A -> X); | - | X = 900 | 3 |
- | write_item(B -> X); | X = 1100 | 4 |
일관성이 깨진 것을 알 수 있다.
원인은 Index2에서 X 값을 읽어온 T2는 T1이 갱신을 준비 중인 데이터를 읽어와 작업을 진행한다.
그리고 Index3 에서 T1이 기록한 데이터를 무시하고 다시 한 번 끼어들어 Index4에서 X 값을 갱신한다.
갱신손실 문제를 해결하려면 상대방의 트랜잭션이 데이터를 사용하는지 여부를 알 수 있는 규칙이 필요하다. 즉, 자신이 데이터를 수정 중이라는 사실을 알리면 된다.
알리는 방식으로 락(Lock)이라는 잠금장치를 사용한다.
락은 자신이 사용할 데이터를 잠그면 다른 트랜잭션은 잠금이 풀릴 때까지 wait 해야한다.
이로써 X에 대한 갱신을 순차적으로 진행할 수 있고, 갱신손실 문제를 해결할 수 있다.
T1 | T2 | 버퍼 값 | Index |
---|---|---|---|
LOCK(X) A = read_item(X); A = A - 100 | - | X = 1000 | 1 |
- | LOCK(X) (wait... 대기) | X = 1000 | 2 |
write_item(A -> X); UNLOCK(X); | - | X = 900 | 3 |
- | B = read_item(X); B = B + 100 write_item(B -> X); UNLOCK(X); | X = 1100 | 4 |
T1 | T2 |
---|---|
START TRANSACTION; USE DB_NAME SELECT FROM Book WHERE bookid = 1; UPDATE Book SET price = 7100 WHERE bookid = 1; SELECT FROM Book WHERE bookid = 1; COMMIT; | START TRANSACTION; USE DB_NAME SELECT FROM Book WHERE bookid = 1; UPDATE Book SET price = price + 100 WHERE bookid = 1; SELECT FROM Book WHERE bookid = 1; COMMIT; |
두 개 이상의 트랜잭션이 각각 자신의 데이터에 대해 락을 획득하고 상대방 데이터에 대해 락을 요청하면 무한 대기 상태에 빠진다.
이러한 현상을 데드락 or 교착상태 라고 한다.
데드락이 발생하면 일반적으로 작업중인 트랜잭션 중 하나를 강제 중지 시킨다. 그 결과 나머지 트랜잭션은 정상적으로 실행된다. 이 때 중지시키는 트랜잭션에서 변경한 데이터는 원래 상태로 rollback한다.