1. 트랜잭션 동시 실행의 문제점
데이터베이스에서 여러 트랜잭션이 동시에 실행될 때, 동일한 데이터에 대해 동시에 읽기와 쓰기를 수행하면 예상치 못한 결과가 발생할 수 있습니다. 이러한 문제를 해결하기 위해 락(Lock)을 사용합니다.
예제 1: Write-Write 충돌
- 상황: 데이터베이스에 X라는 데이터가 있으며, 값은 10입니다. 두 개의 트랜잭션(T1, T2)이 각각 X의 값을 20과 90으로 변경하려고 합니다.
- 문제: 두 트랜잭션이 동시에 실행되면 X의 값이 예기치 않은 상태가 될 수 있습니다. 이를 방지하기 위해 락을 사용하여 한 트랜잭션이 완료될 때까지 다른 트랜잭션이 대기하도록 합니다.
예제 2: Write-Read 충돌
- 상황: T1이 X의 값을 20으로 변경하려고 하고, T2는 X의 값을 읽으려고 합니다.
- 문제: T1이 완료되기 전에 T2가 X의 값을 읽으면 잘못된 값이 반환될 수 있습니다. 이를 방지하기 위해 T1이 락을 해제할 때까지 T2가 대기해야 합니다.
2. 락의 종류와 동작 방식
2.1. 쓰기 락 (배타적 락)
- 설명: 쓰기 락은 데이터를 수정할 때 사용되며, 다른 트랜잭션이 동일한 데이터에 대해 읽거나 쓰기를 할 수 없도록 합니다. 이는 데이터의 무결성을 보호하는 역할을 합니다.
2.2. 읽기 락 (공유 락)
- 설명: 읽기 락은 데이터를 읽을 때 사용되며, 여러 트랜잭션이 동시에 동일한 데이터를 읽을 수 있도록 허용합니다. 하지만, 다른 트랜잭션이 해당 데이터를 수정하려고 할 경우 읽기 락이 해제될 때까지 대기해야 합니다.
3. 2단계 락킹 (2PL) 프로토콜
2PL은 트랜잭션에서 락을 관리하는 프로토콜로, 트랜잭션의 직렬화 가능성을 보장합니다. 2PL은 두 가지 단계로 나뉩니다.
- 확장 단계: 트랜잭션이 필요한 모든 락을 획득하는 단계로, 락을 해제하지 않습니다.
- 축소 단계: 트랜잭션이 더 이상 락을 획득하지 않고, 모든 락을 해제하는 단계입니다.
예제 3: 2PL을 통한 트랜잭션 관리
- 상황: 데이터베이스에 X, Y, Z가 있으며, T1은 X와 Y의 값을 더해 Y에 저장하고, X의 값을 두 배로 해서 Z에 저장하는 트랜잭션입니다.
- 2PL 적용: T1은 먼저 필요한 모든 락을 획득한 후 트랜잭션을 시작하고, 이후 락을 해제하며 트랜잭션을 종료합니다.
4. 2PL의 확장: 보수적 2PL, 엄격한 2PL, 강한 엄격한 2PL
4.1. 보수적 2PL
- 설명: 모든 락을 획득한 후에 트랜잭션을 시작합니다. 교착 상태가 발생하지 않지만, 락을 모두 확보해야 트랜잭션이 시작되므로 비효율적일 수 있습니다.
4.2. 엄격한 2PL
- 설명: 쓰기 락을 획득한 트랜잭션은 커밋 또는 롤백이 될 때까지 락을 해제하지 않습니다. 이는 복구 가능성을 보장합니다.
4.3. 강한 엄격한 2PL (SS2PL)
- 설명: 읽기 락과 쓰기 락 모두 커밋 또는 롤백이 될 때까지 해제되지 않습니다. SS2PL은 구현이 쉽고, 모든 트랜잭션의 동작을 직렬화할 수 있는 장점이 있지만, 락을 오래 유지하여 성능에 영향을 줄 수 있습니다.
5. 2PL의 한계와 MVCC
2PL은 읽기와 쓰기가 서로를 차단하는 경우 처리량이 떨어지는 문제가 있습니다. 이를 해결하기 위해 MVCC(다중 버전 동시성 제어)가 도입되었습니다. MVCC는 락과 혼용하여 사용되며, 동시성을 높이는 데 중요한 역할을 합니다. 다음 포스팅에서는 MVCC의 작동 방식을 살펴보겠습니다.
결론
이번 포스팅에서는 락을 사용하여 트랜잭션의 동시성을 제어하는 방법과, 이를 통해 트랜잭션의 직렬화 가능성과 격리성을 보장하는 2PL 프로토콜에 대해 알아보았습니다. 2PL은 트랜잭션의 무결성을 보장하는 강력한 도구이지만, 성능 측면에서는 한계가 있습니다. 이를 보완하는 MVCC에 대해서는 다음 포스팅에서 더 깊이 탐구해보겠습니다.
출처
https://www.youtube.com/watch?v=aL0XXc1yGPs&list=PLcXyemr8ZeoREWGhhZi5FZs6cvymjIBVe