DB 트랜잭션 동시성제어와 일관성 유지 방식
동시성제어의 직렬성 기준과 갱신 손실, 더티 리드 문제를 정리하고 2PL·타임스탬프·낙관적 검증 기법의 선택 기준을 설명한다.
2026-08-14 · 최초 발행 2025-08-10
병행 실행 결과를 직렬 실행처럼 유지하는 일
여러 사용자의 트랜잭션이 같은 데이터베이스를 동시에 건드리면, 처리량만 높인다고 원하는 결과가 나오지는 않는다. 동시성제어는 트랜잭션 연산이 인터리빙되는 상황에서도 데이터 무결성과 일관성을 유지하도록 만드는 기술이다. 사용자에게는 작업이 동시에 처리되는 것처럼 보이되, 결과는 일관된 상태에 남아야 한다.
현대 정보시스템에서는 동시에 수천, 수만 개의 트랜잭션이 처리되므로 이 제어는 선택 사항이 아니다. 핵심 목표는 트랜잭션의 직렬성을 보장하는 데 있다.
스케줄과 직렬성이 판단 기준이 되는 이유
트랜잭션 스케줄은 각 트랜잭션의 연산이 시간 순서에 따라 배치된 실행 순서다. 여러 트랜잭션의 읽기와 쓰기가 서로 섞여 실행되는 형태를 표현한다.
직렬 스케줄에서는 트랜잭션이 겹치지 않고 순차적으로 수행된다. 반면 비직렬 스케줄은 트랜잭션이 인터리빙되어 진행된다. 실제 시스템은 병행성을 위해 비직렬 스케줄을 사용하지만, 그 결과는 직렬 스케줄의 결과와 같아야 한다.
이 특성을 직렬성(Serializability)이라 한다.
- 충돌 직렬가능성은 충돌하는 연산의 순서가 보존되는 경우다.
- 뷰 직렬가능성은 데이터의 읽기와 쓰기 관점에서 동일한 결과가 만들어지는 경우다.
제어가 없을 때 깨지는 데이터 상태
갱신 손실
여러 트랜잭션이 같은 값을 동시에 수정하면, 나중에 기록된 결과가 먼저 수행한 갱신을 덮어쓸 수 있다.
트랜잭션 T1: R(A) → A=A+100 → W(A)
트랜잭션 T2: R(A) → A=A+200 → W(A)
순서대로 처리했다면 A는 300 증가해야 한다. 그러나 병행 실행에서는 200만 증가할 수 있다.
더티 리드
아직 완료되지 않은 트랜잭션의 중간 결과를 다른 트랜잭션이 읽는 경우다.
트랜잭션 T1: W(A) → Rollback
트랜잭션 T2: R(A) → 처리 → Commit
T2는 T1이 기록한 임시 데이터를 기반으로 처리했지만, T1이 롤백되면 그 처리의 전제가 사라진다.
일관성 없는 읽기
하나의 트랜잭션이 같은 데이터를 반복해서 읽는 사이 다른 트랜잭션이 값을 바꾸면, 트랜잭션 내부에서 서로 다른 값을 보게 된다.
트랜잭션 T1: R(A) → ... → R(A)
트랜잭션 T2: W(A)
T1의 첫 읽기와 두 번째 읽기 사이에 T2가 A를 수정하면, T1은 일관되지 않은 값을 읽는다.
연쇄 복귀
한 트랜잭션의 실패가 그 결과에 의존한 후속 트랜잭션의 롤백까지 이어지는 현상이다.
트랜잭션 T1: W(A) → Rollback
트랜잭션 T2: R(A) → W(B) → Commit
트랜잭션 T3: R(B) → W(C) → Commit
T1이 롤백되면 A를 읽은 T2도 롤백되어야 하고, T2의 결과를 읽은 T3도 함께 복귀해야 한다. 복구 작업은 복잡해지고 시스템 성능도 낮아진다.
잠금의 획득과 해제를 분리하는 2PL
2단계 잠금(2 Phase Locking, 2PL)은 Lock과 Unlock을 확장 단계와 수축 단계로 나누는 프로토콜이다. 모든 잠금 연산은 모든 해제 연산보다 먼저 일어나야 한다.
확장 단계에서는 새 락을 획득할 수 있지만 해제할 수 없다. 수축 단계에 들어가면 락을 해제할 수 있지만 새 락은 얻을 수 없다.
엄격한 2PL(Strict 2PL)은 모든 쓰기 락을 트랜잭션 완료 시점까지 유지한다. 강한 엄격한 2PL(Rigorous 2PL)은 읽기 락과 쓰기 락 모두를 완료 시점까지 유지한다.
2PL은 직렬가능성을 보장하고 구현이 비교적 단순하다. 다만 교착상태(Deadlock)가 발생할 수 있으므로, 감지와 해결 메커니즘이 필요하다.
타임스탬프로 실행 순서를 고정하는 방식
타임스탬프 기반 프로토콜(Timestamp Ordering Protocol)은 각 트랜잭션에 고유 타임스탬프를 부여하고, 이를 기준으로 실행 순서를 미리 정한다. 타임스탬프는 시스템 시계나 논리적 계수기로 발급한다.
데이터 항목에는 읽기 타임스탬프(Read_TS)와 쓰기 타임스탬프(Write_TS)를 둔다. Read_TS는 해당 데이터를 읽은 가장 최근 트랜잭션의 타임스탬프이며, Write_TS는 데이터를 수정한 가장 최근 트랜잭션의 타임스탬프다.
트랜잭션 T가 데이터 항목 Q를 읽으려 할 때 TS(T) < Write_TS(Q)이면 이미 수정된 데이터를 읽으려는 것이므로 롤백한다. TS(T) ≥ Write_TS(Q)이면 읽기를 허용하고 Read_TS(Q) = max(TS(T), Read_TS(Q))로 갱신한다.
T가 Q를 쓰려 할 때는 TS(T) < Read_TS(Q)이거나 TS(T) < Write_TS(Q)이면 롤백한다. 어느 조건에도 해당하지 않으면 쓰기를 허용하고 Write_TS(Q) = TS(T)로 갱신한다.
이 방식은 실행 순서가 사전에 정해지므로 교착상태를 방지하며 직렬성을 보장한다. 대신 연쇄 복귀가 발생할 수 있고, 롤백이 잦아지면 오버헤드가 커질 수 있다.
충돌이 드문 작업에서의 낙관적 검증
낙관적 동시성제어(Optimistic Concurrency Control)는 트랜잭션을 수행하는 동안 별도 검사를 하지 않고, 종료 시점에 변경사항을 일괄 검증한다. 대부분의 트랜잭션이 충돌 없이 실행된다는 가정에 기반한다.
읽기 단계에서는 필요한 데이터를 읽어 로컬 워크스페이스에서 작업한다. 검증 단계에서는 변경사항이 데이터베이스 일관성을 해치지 않는지 확인한다. 검증이 성공하면 쓰기 단계에서 로컬 워크스페이스의 변경사항을 실제 데이터베이스에 반영한다.
검증에는 타임스탬프와 읽기 집합·쓰기 집합의 교차 여부를 사용한다. 다음 조건 중 하나라도 충족해야 검증에 성공한다.
- Ti가 Tj의 쓰기 단계 전에 완료한다.
- Ti의 쓰기 집합이 Tj의 읽기 집합과 교차하지 않고, Ti의 쓰기 단계 전에 Tj가 완료한다.
- Ti의 쓰기 집합이 Tj의 읽기 집합 및 쓰기 집합과 교차하지 않고, Ti의 읽기 단계가 Tj의 쓰기 단계 전에 완료한다.
읽기 작업이 많고 충돌이 적은 환경에서는 락 오버헤드 없이 높은 병렬성을 제공할 수 있다. 반대로 검증에 실패하면 롤백 비용이 발생하며, 트랜잭션이 길어질수록 검증 실패 확률도 증가한다.
환경에 따라 달라지는 기법 선택
| 기법 | 장점 | 단점 | 적합한 환경 |
|---|---|---|---|
| 2PL | 구현 단순, 직렬성 보장, 실시간 충돌 감지 | 교착상태 발생 가능, 병행성 제한, 락 오버헤드 | 쓰기 작업이 많고 충돌 가능성이 높은 환경 |
| 타임스탬프 | 교착상태 방지, 직렬성 보장, 트랜잭션 순서 보장 | 연쇄 복귀 가능, 롤백 빈번, 타임스탬프 관리 오버헤드 | 짧은 트랜잭션, 교착상태를 피해야 하는 환경 |
| 낙관적 검증 | 락 오버헤드 없음, 높은 병행성, 교착상태 방지 | 검증 실패 시 롤백, 긴 트랜잭션에 불리, 충돌 시 비용 증가 | 읽기 작업이 많고 충돌 가능성이 낮은 환경 |
은행 계좌 이체 시스템에서는 계좌 A와 B의 락을 모두 획득한 뒤 이체를 수행하는 2PL을 적용할 수 있다. 교착상태를 피하기 위해 계좌 번호 순서로 락을 얻고, 트랜잭션이 끝날 때까지 락을 유지한다.
항공권 예약 시스템은 대부분이 검색이고 실제 예약은 적다는 특성 때문에 낙관적 동시성제어를 적용할 수 있다. 예약 시점의 검증 단계에서 좌석 가용성을 최종 확인하고, 충돌이 나면 사용자에게 재시도를 요청한다.
데이터 웨어하우스에서는 읽기 작업이 많고 일관된 분석 결과가 필요할 때 타임스탬프 기반 기법을 적용할 수 있다. 여러 분석 쿼리가 동시에 실행되어도 일관된 결과를 제공하며, 쓰기 작업은 특정 시간의 일괄 처리 방식으로 수행한다.
동시성제어 기법은 읽기와 쓰기 비율, 트랜잭션 길이, 충돌 가능성을 함께 보고 선택한다. DBMS는 여러 기법을 혼합하거나 트랜잭션 격리 수준을 통해 성능과 일관성 사이의 균형을 조정할 수 있다.