Transaction & Concurrency Control Mechanics
트랜잭션과 동시성 제어 메커니즘의 정의, 범위, 선행 지식, 학습 주제, 참고 근거를 정리한 CS&E 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
data-information-managementdatainformation-managementrelational-systemstransactionconcurrency-control-mechanicsdatabasesdatabase-internals10 min read
1. Overview
트랜잭션과 동시성 제어(Transaction & Concurrency Control Mechanics)는 수천 명의 사용자가 동시에 같은 데이터에 접근하여 읽고 쓰는 경합 환경에서, 데이터베이스가 데이터의 무결성과 일관성을 보장하는 동기화 메커니즘을 다룹니다.
학습자는 트랜잭션의 4대 원칙인 **ACID(원자성, 일관성, 격리성, 지속성)**의 개념을 넘어, 이를 구현하는 Undo Log(롤백용)와 Redo Log(복구용)의 역할을 살펴봅니다. 나아가 동시성 경합 시 발생하는 다양한 데이터 훼손(Dirty Read, Non-Repeatable Read, Phantom Read)과 이를 방어하기 위한 **격리 수준(Isolation Levels)**을 정리합니다. 마지막으로 전통적인 비관적 락(Pessimistic Lock)의 병목을 해결하고 읽기와 쓰기가 서로를 차단하지 않도록 진화한 현대 RDBMS의 코어 기술, 다중 버전 동시성 제어(MVCC, Multi-Version Concurrency Control) 엔진을 익혀 견고한 뱅킹/결제 시스템 설계 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- ACID 메커니즘 (ACID Mechanics): Atomicity(트랜잭션 로그/Undo), Consistency(제약조건), Isolation(격리수준), Durability(Redo/WAL, 커밋).
- 데이터 경쟁 이상 현상 (Concurrency Anomalies): Dirty Read, Non-Repeatable Read, Phantom Read, Lost Update.
- 격리 수준 (Isolation Levels): Read Uncommitted, Read Committed, Repeatable Read, Serializable.
- 동시성 제어 엔진 (Concurrency Control): 락(Lock: Shared/Exclusive, Row/Table 레벨), 낙관적 락(Optimistic Lock), 비관적 락(Pessimistic Lock), MVCC(Snapshot Isolation).
Out-of-Scope
- 분산 트랜잭션 (Distributed Transactions): MSA 환경에서의 2PC(Two-Phase Commit)나 Saga 패턴 → 07-02-04 Distributed Transactions 영역으로 위임.
- 데이터베이스 복제/HA (Replication/HA): Master-Slave(Primary-Replica) 복제 지연 및 동기화 → 06-03 Distributed Logic 또는 시스템 아키텍처 영역.
Boundaries
- 격리성(Isolation) vs 동시성(Concurrency) 성능:
Serializable격리 수준은 트랜잭션을 직렬화하여 이상 현상을 차단하지만, 락(Lock) 대기로 인해 트랜잭션 처리량(TPS)이 크게 낮아질 수 있습니다. 반면Read Uncommitted는 빠르지만 데이터 신뢰성이 낮습니다. 대부분의 RDBMS는 성능과 정합성의 타협점인Read Committed(Oracle/PostgreSQL 기본) 또는Repeatable Read(MySQL InnoDB 기본)를 선택하며, MVCC 기법을 통해 락 대기를 줄이면서 높은 수준의 격리성과 동시성을 함께 확보합니다.
3. Counterexample
- Lost Update (갱신 손실) 버그 (Lost Update): 두 스레드가 동시에
SELECT balance FROM account WHERE id=1(결과 100). 스레드 A가 10을 더해 110을UPDATE하고 커밋하려 함. 거의 동시에 스레드 B가 20을 더해 120을UPDATE하고 커밋. 최종 결과는 스레드 A의 연산이 무시되고 120이 됩니다. (정상이라면 130이어야 함). 기본 격리 수준에서는 방어되지 않으며,SELECT ... FOR UPDATE(비관적 락) 또는 데이터베이스 버저닝(낙관적 락,WHERE version=?)을 통해 방어해야 하는 전형적인 결제 시스템 사고입니다. - Long Transaction에 의한 자원 고갈 및 Undo 버퍼 증가: 배치 프로그램에서 1시간 동안 하나의 트랜잭션을 열어두고 수백만 건을 읽고 쓰는 경우. 트랜잭션이 열려있는 동안 MVCC 구조 하에서 변경된 모든 레코드의 이전 버전(Undo Log)이 삭제(Purge)되지 못하고 디스크/메모리에 계속 쌓입니다. 다른 트랜잭션들의 성능이 낮아지고, DB 디스크가 가득 차거나 시스템 테이블스페이스(ibdata) 사이즈가 커지는 장애가 발생할 수 있습니다. 트랜잭션은 반드시 가장 짧은 범위로 분할 커밋(Chunking)해야 합니다.
4. Prerequisites
- 운영체제 동기화 (Basic): 락(Mutex)과 임계 구역, 데드락 개념. (03-02-02 Thread Synchronization)
- SQL DML (Basic):
BEGIN,COMMIT,ROLLBACK,SELECT,UPDATE문법 이해.
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: All or Nothing, 장애를 견디는 ACID와 로깅 (ACID & WAL)
- Why to Learn: 애플리케이션 코드가 중간에 예외를 던지거나 서버 전원이 갑자기 끊겨도, "이체 중 돈이 사라지는 상황"을 막아내는 트랜잭션 엔진의 하부 물리 로직을 이해하기 위함입니다.
- What to Learn:
- Concepts: ACID(Atomicity, Consistency, Isolation, Durability), 트랜잭션(Transaction), Commit, Rollback, WAL(Write-Ahead Logging), Undo Log, Redo Log.
- Skills: 트랜잭션 경계 설정, 비즈니스 실패 시 명시적 Rollback.
- How to Learn:
- 1단계: 원자성과 Undo: 트랜잭션 중간에 3건을 업데이트하다가 4건째에 에러. RDBMS는
UPDATE시 디스크에 덮어쓰기 전에 옛날 값을 Undo Log 버퍼에 기록해둡니다.ROLLBACK시 이 Undo Log를 역순으로 읽어(Undo) 데이터를 에러 이전 원상태로 돌리는 방식을 살펴봅니다. - 2단계: 지속성과 WAL(Write-Ahead Log): 트랜잭션이
COMMIT될 때, 느린 디스크의 실제 데이터 페이지를 갱신하는 대신, 빠르고 순차적인 디스크 로깅 파일(WAL, Redo Log)에 "변경 내역"만 즉시 기록(Flush)합니다. 1초 뒤 전원이 나가도 재부팅 시 이 로그를 읽어 데이터베이스를 복구하는 내구성 메커니즘을 살펴봅니다.
- 1단계: 원자성과 Undo: 트랜잭션 중간에 3건을 업데이트하다가 4건째에 에러. RDBMS는
- Implement: 파이썬 객체 지향 트랜잭션 시뮬레이터 구현.
Database클래스와Transaction객체.update(key, val)실행 시 현재 값을undo_log스택에 푸시.commit()호출 시 변경 확정.rollback()호출 시undo_log스택을 팝하며 기존 데이터를 복원하는 원자성 달성 데모 스크립트.
Recommended
Core Topic 02: 서로를 방해하는 트랜잭션, 이상 현상과 격리 수준 (Anomalies & Isolation Levels)
- Why to Learn: 멀티스레드와 동일하게 DB 트랜잭션들이 겹칠 때 데이터가 어떻게 어긋나는지(Anomaly)를 이해하고, ANSI 표준 4단계 격리 수준이 각각 무엇을 허용하고 무엇을 막는지 트레이드오프를 익히기 위함입니다.
- What to Learn:
- Concepts: Dirty Read(커밋 안 된 데이터 읽기), Non-repeatable Read(같은 쿼리 연속 실행 시 중간에 수정 발생), Phantom Read(중간에 삽입/삭제로 유령 행 발생), 4가지 격리 수준(Read Uncommitted, Read Committed, Repeatable Read, Serializable).
- Skills: 격리 수준 변경 쿼리, 서비스 요건에 따른 격리 수준 선택.
- How to Learn:
- 1단계: 이상 현상 시나리오: 트랜잭션 A가 내 잔고를 100원에서 0원으로 뺌. (커밋 전). 트랜잭션 B가 잔고를 읽으니 0원이라 출금 거절. 직후 트랜잭션 A가 에러로 롤백. B는 커밋되지 않은 Dirty 데이터를 읽어 잘못된 비즈니스 판단을 내림(Dirty Read). 이를 막는 것이
Read Committed격리 수준임을 살펴봅니다. - 2단계: Repeatable Read vs Serializable: 트랜잭션 내내 같은 행을 조회하면 값이 같음을 보장(Repeatable). 하지만 A가 "10대 유저수" 10명 집계 중, B가 10대 유저를
INSERT. A가 다시 집계하면 11명이 나오는 유령 현상(Phantom Read). 이를 차단하는 최고 수준Serializable의 락 비용을 살펴봅니다.
- 1단계: 이상 현상 시나리오: 트랜잭션 A가 내 잔고를 100원에서 0원으로 뺌. (커밋 전). 트랜잭션 B가 잔고를 읽으니 0원이라 출금 거절. 직후 트랜잭션 A가 에러로 롤백. B는 커밋되지 않은 Dirty 데이터를 읽어 잘못된 비즈니스 판단을 내림(Dirty Read). 이를 막는 것이
- Implement: 두 개의 터미널(Python 콘솔 2개)에서 DB(MySQL 등) 동시 접속. 트랜잭션 1에서 커밋 전
UPDATE, 트랜잭션 2에서 락 대기 혹은 Dirty Read 여부 확인. 격리 수준을READ UNCOMMITTED에서REPEATABLE READ로 변경해가며 이상 현상이 재현/방어되는 과정을 직접 눈으로 확인하고 로그 스크립트로 남김.
Practical
Core Topic 03: 읽기와 쓰기의 충돌, 락 기반 동시성 제어 (Pessimistic & Optimistic Locking)
- Why to Learn: 선착순 쿠폰 발급, 티켓 예매, 뱅킹 송금 등 정합성이 중요한 크리티컬 섹션을 락(Lock)을 통해 보호하는 실무 엔지니어링을 갖추기 위함입니다.
- What to Learn:
- Concepts: 공유 락(Shared Lock, S, 읽기 전용), 배타 락(Exclusive Lock, X, 쓰기 전용), 락 호환성(S-S 호환, S-X/X-X 대기), 데드락(Deadlock), 비관적 락(
SELECT ... FOR UPDATE), 낙관적 락(Version 컬럼 검증). - Skills: 데드락 원인(교차 락 획득) 분석, 동시성 요구사항에 따른 낙관/비관 락 선택 설계.
- Concepts: 공유 락(Shared Lock, S, 읽기 전용), 배타 락(Exclusive Lock, X, 쓰기 전용), 락 호환성(S-S 호환, S-X/X-X 대기), 데드락(Deadlock), 비관적 락(
- How to Learn:
- 1단계: 비관적 락(
FOR UPDATE): 데이터 경쟁이 아주 빈번할 것으로 "비관적으로" 가정. 데이터를 읽을 때부터 X락을 걸어버림. 다른 트랜잭션은 읽기조차 대기. 갱신 손실(Lost Update)을 강하게 방어하지만, 시스템 처리량(TPS)에 큰 병목이 생기는 방식을 살펴봅니다. - 2단계: 낙관적 락(Optimistic): 거의 충돌 안 날 것으로 "낙관적으로" 가정. DB 락 없이 일단 읽음. 데이터에
version=1컬럼 추가. 수정 후 커밋 시UPDATE ... WHERE id=1 AND version=1. 만약 다른 트랜잭션이 먼저 커밋해서 버전을 올렸다면 이UPDATE는 0건 적용됨(실패). 애플리케이션에서 재시도(Retry) 처리. DB 부하가 적은 현대적 설계를 살펴봅니다.
- 1단계: 비관적 락(
- Implement: 파이썬 ThreadPool과 SQLAlchemy 활용. 잔고 1000원 계좌에 100개 스레드가 동시 10원 차감 로직 실행 시뮬레이터. 1) 동시성 제어 없음 (잔고 >0처럼 잘못된 결과 발생). 2) 낙관적 락
Version컬럼 기반 업데이트 로직 (버전 불일치 시try-except+ 재시도 루프 로직). 모든 스레드 완료 후 잔고가 0원이 되는 락킹 구조 데모.
Advanced
Core Topic 04: 읽기는 쓰기를 막지 않는다, 다중 버전 동시성 제어 (MVCC)
- Why to Learn: "A가 행을 Update 중(X락)일 때, B가 그 행을 Select 하면 대기(Wait)해야 하는가?" 고전 RDBMS는 대기했지만, Oracle/MySQL/PostgreSQL 등 현대 DB는 대기하지 않는 구조를 제공합니다. MVCC 내부 아키텍처를 이해하여 데이터베이스 성능 튜닝의 기반을 다지기 위해서입니다.
- What to Learn:
- Concepts: MVCC(Multi-Version Concurrency Control), 스냅샷 격리(Snapshot Isolation), Undo 영역(Rollback Segment) 활용, 트랜잭션 ID(TXID)와 가시성(Visibility) 규칙, 갭 락(Gap Lock)과 넥스트 키 락(Next-Key Lock, InnoDB).
- Skills: 트랜잭션 ID 기반 레코드 추적, 불필요한 Phantom Read가 InnoDB에서는 MVCC로 어떻게 거의 방어되는지 이해.
- How to Learn:
- 1단계: 버전 체인(Version Chain): 트랜잭션 TX2가 잔고를 100 200으로 업데이트하면(디스크엔 200 기록, 커밋 전), 100이라는 구버전(Old Version) 데이터를 Undo 영역에 넣고 포인터로 연결합니다. 트랜잭션 TX1이 이때 잔고를 조회하면, TX2의 미커밋 데이터(200)가 아니라, Undo 영역에 있는 TX1 시작 시점의 스냅샷 데이터(100)를 찾아 락 대기 없이 읽어가는 방식을 살펴봅니다.
- 2단계: 가시성(Visibility): "나는 지금 트랜잭션 ID가 50번이야. 디스크의 행을 읽었더니 최근 수정자가 TXID 55(미래)네? 나는 볼 수 없는(Invisible) 미래 데이터군. Undo 로그를 뒤져서 TXID 50보다 과거에 확정된 버전을 찾아 읽자." 이것이 격리 수준(Repeatable Read)을 유지하며 동시성을 높이는 MVCC의 핵심 논리임을 살펴봅니다.
- Implement: 메모리 기반 미니 MVCC 데이터베이스 엔진 파이썬 객체 구현. 데이터 레코드는 값 대신 리스트
[ (TxID, Val), (TxID, Val) ]구조. 전역 변수global_txid를 발급받는 트랜잭션 A(ID 10) 시작. 트랜잭션 B(ID 11)가 값 수정. 트랜잭션 A가 읽기를 시도할 때, 버전 리스트를 순회하며자신 트랜잭션 ID(10) 이하이면서 가장 큰 커밋된 데이터를 찾아 반환하는 가시성 판단(Visibility Check) 함수 시뮬레이션 데모.
7. Terminology
8. References
Primary
- [P1] CS2023 - Information Management (IM) - Transaction Processing
- [P5] SFIA - Database Administration (DBAD) - Concurrency Control
Secondary
- [Transaction Processing: Concepts and Techniques] Jim Gray - The foundation of ACID and Locking
- [Designing Data-Intensive Applications] Martin Kleppmann - Transactions & Distributed Data
Industry
- [MySQL 8.0 Reference Manual] - InnoDB Transaction Model and Locking
- [PostgreSQL Documentation] - Concurrency Control & MVCC
9. Final Checklist
Primary
- 트랜잭션 중간에 에러가 났을 때 Undo Log를 통해 원자성(Atomicity)이 복구되는 과정을 설명할 수 있는가?
- 디스크에 실제 데이터를 쓰기 전에 WAL(Write-Ahead Log)에 먼저 기록하여 지속성(Durability)을 확보하는 이유를 증명할 수 있는가?
Secondary
- Dirty Read, Non-Repeatable Read, Phantom Read 이상 현상 간의 차이를 실제 트랜잭션 타임라인으로 묘사할 수 있는가?
-
SELECT ... FOR UPDATE구문(비관적 락)이 유발할 수 있는 데드락(Deadlock)과 성능 병목을 설명할 수 있는가?
Industry
- MVCC 환경에서 조회 쿼리(Select)가 락 대기 없이 과거 버전의 스냅샷을 읽어내는 매커니즘을 논증할 수 있는가?
- 높은 동시 접속 환경(예: 선착순 티켓팅)에서 비관적 락 대신 낙관적 락(Version 컬럼)을 선택해야 하는 기준을 설계할 수 있는가?