트랜잭션
개요
트랜잭션(transaction)은 관련된 데이터 조작을 하나의 작업 단위로 취급하는 개념이다. 계좌 이체에서 보내는 계좌의 잔액 감소와 받는 계좌의 잔액 증가를 따로 확정하면, 둘 사이의 장애로 돈이 사라지거나 늘어날 수 있다. 두 변경을 같은 작업의 일부로 묶으면 전부 확정하거나 전부 취소하는 기준을 세울 수 있다.
데이터베이스에서는 여러 문장으로 표현된 작업도 하나의 트랜잭션에 들어갈 수 있다. 작업이 성공해 결과를 확정하는 동작이 커밋(commit)이고, 진행 중인 변경을 취소하는 동작이 롤백(rollback)이다. 사용자에게 보이는 주문 한 건이 DB 트랜잭션 하나와 정확히 일치하는지는 애플리케이션의 경계 설정에 달려 있다.
묶어서 확정하는 성질만으로는 동시 실행까지 설명할 수 없다. 같은 좌석을 두 사람이 동시에 예약하거나, 서로 다른 행을 바꾸는 두 작업이 함께 하나의 규칙을 어기는 문제도 있다. 트랜잭션은 복구할 작업 단위와 함께, 여러 작업이 겹칠 때 어떤 실행을 허용할지도 다루며 이 둘은 원자성과 격리성이라는 다른 질문으로 나뉜다.
이 문서는 트랜잭션을 특정 DB의 BEGIN 문법에서 출발한 기능으로 보지 않고, 장애를 견디는 데이터 처리의 단위를 어떻게 정의해 왔는지부터 살펴본다. SQL 격리 수준과 MVCC는 그 개념을 구현하고 조정하는 과정에서 만나는 용어다.
공유 데이터의 일관성을 실행 순서와 연결하다 (1976)
여러 사용자가 같은 데이터를 다루면 개별 작업의 올바름과 전체 실행의 올바름이 갈라진다. 재고를 읽고 줄이는 절차를 따로 보면 맞아도, 두 작업이 읽기와 쓰기 사이에 끼어들면 결과가 달라진다. 트랜잭션의 역사는 복구할 단위와 함께 실행 순서를 정의하는 문제를 포함한다.
Eswaran·Gray·Lorie·Traiger의 1976년 논문은 트랜잭션·일관성·스케줄과 잠금의 관계를 형식화했다. 어떤 잠금 규칙 아래 여러 작업을 섞어 실행해도 직렬 실행과 동등한 결과를 얻는지 분석하고, 물리적인 레코드뿐 아니라 조건으로 지정한 데이터 집합을 보호하는 문제를 다뤘다.1
이 기록에서 트랜잭션은 SQL 명령 하나의 이름보다 넓은 작업 단위다. 이후의 복구와 격리 논의도 무엇을 한 작업으로 보고 어떤 실행을 허용할 것인가라는 두 질문을 이어받는다.
상태 변환과 복구 범위를 정리하다 (1981)
Jim Gray의 「The Transaction Concept: Virtues and Limitations」는 1981년 트랜잭션을 원자성·일관성·내구성을 가진 상태 변환으로 설명했다. 이미 발전하던 개념과 구현을 정리하면서, 하위 트랜잭션과 오래 지속되는 작업처럼 남아 있는 문제도 논의했다.2
논문은 보호되는 데이터 동작과 외부 세계의 동작을 구별한다. DB 변경을 되돌리는 것과 이미 배출한 현금을 되찾는 것은 다른 종류의 일이다. 이 구분은 훗날 원격 서비스의 호출이나 메시지 전달을 로컬 롤백과 어떻게 연결할 것인지 이해하는 데도 쓰인다.
복구 시스템은 어떤 변경이 같은 작업에 속하고 무엇이 이미 확정됐는지 알아야 한다. 작업 경계와 커밋 기록이 단순한 제어 문법을 넘어 복구 판단의 근거가 되는 이유다.
복구 기법을 비교하는 언어로 묶인 ACID (1983)
Theo Härder와 Andreas Reuter의 1983년 논문은 복구 기법들을 같은 개념으로 비교하기 위한 틀을 제시하고, 원자성·일관성·격리성·내구성을 ACID로 묶어 설명했다.3
성질 | 다루는 문제 |
|---|---|
Atomicity, 원자성 | 일부 변경만 확정되는 일을 막고 작업의 전부 또는 취소를 정한다 |
Consistency, 일관성 | 선언한 무결성·업무 규칙을 유지하는 상태 변환을 요구한다 |
Isolation, 격리성 | 함께 실행되는 트랜잭션 사이의 관찰과 영향을 다룬다 |
Durability, 내구성 | 커밋된 결과를 저장·복구 계약에 따라 유지한다 |
- 성질
Atomicity, 원자성
- 다루는 문제
일부 변경만 확정되는 일을 막고 작업의 전부 또는 취소를 정한다
- 성질
Consistency, 일관성
- 다루는 문제
선언한 무결성·업무 규칙을 유지하는 상태 변환을 요구한다
- 성질
Isolation, 격리성
- 다루는 문제
함께 실행되는 트랜잭션 사이의 관찰과 영향을 다룬다
- 성질
Durability, 내구성
- 다루는 문제
커밋된 결과를 저장·복구 계약에 따라 유지한다
로그·체크포인트·변경을 저장 장치에 반영하는 전략은 이 성질들을 구현하는 방법으로 비교된다. 필요한 로그를 데이터 변경보다 먼저 보존하는 WAL 규칙도 복구의 순서를 설명하는 핵심이다. 일관성에서 요구하는 업무 규칙은 스키마와 애플리케이션이 표현해야 한다. DB는 잘못 작성한 계산의 의도를 대신 알아내지 않는다.
이 시기의 정리는 트랜잭션을 발명한 한 순간을 뜻하기보다, 서로 다른 복구 구현을 같은 질문으로 설명할 용어가 정리된 과정이다.
SQL 격리 수준의 설명을 다시 묻다 (1995)
SQL의 격리 수준은 dirty read, nonrepeatable read, phantom 같은 이상 현상으로 설명돼 왔다. Berenson 등의 1995년 「A Critique of ANSI SQL Isolation Levels」는 당시 정의의 모호함과 빠진 경우를 분석하고 스냅샷 격리를 다뤘다.4
여기서 중요한 차이는 안정적인 스냅샷과 직렬가능성 사이에 있다. 같은 시점의 데이터를 읽어도 서로 다른 행을 쓰는 두 트랜잭션이 함께 규칙을 깨뜨릴 수 있다. 이 쓰기 왜곡은 “같은 행을 덮어쓰지 않았다”만으로 동시 실행의 올바름을 판정할 수 없음을 보여준다.
직렬가능성은 성공한 작업들의 효과가 어떤 직렬 순서와 같아야 한다는 요구다. 실제 구현은 작업을 겹쳐 실행하면서도 충돌을 기다리게 하거나 실행을 중단해 이 요구를 맞출 수 있다.
스냅샷 위에서 직렬가능성을 구현한 경험 (2012)
Dan R. K. Ports와 Kevin Grittner의 2012년 논문은 PostgreSQL 9.1의 Serializable Snapshot Isolation 구현 경험을 설명한다. 이 연도는 논문 발표 시점이며, 구현이 제공하는 격리와 그 비용·충돌 검사를 읽을 근거다.5
논문의 당직 예에서는 두 트랜잭션이 각각 두 명의 당직자를 읽고 자기 행만 근무 종료로 바꿀 수 있다. 스냅샷 격리에서는 쓰기 대상이 달라도 최종 당직자가 0명이 되는 실행이 가능하다. SSI는 읽기·쓰기 의존성을 추적해 직렬가능성을 깨뜨릴 실행을 중단하는 방식으로 이 문제를 다룬다.5
MVCC가 여러 버전을 관리하는 기법이라면, 읽을 버전과 거절할 충돌은 격리 규칙의 선택이다. PostgreSQL 18에서도 Read Committed·Repeatable Read·Serializable의 관찰 결과는 다르며, 직렬화 실패 뒤에는 호출자가 실패한 트랜잭션 전체를 다시 판단할 처리가 필요하다.6
로컬 작업 단위에서 서비스 간 업무로
트랜잭션의 적용 대상이 여러 서비스로 넓어지면 DB의 작업 단위와 사용자에게 보이는 업무가 서로 다른 길이를 갖게 된다. 한 DB의 주문과 재고는 로컬 트랜잭션에 넣을 수 있지만, 다른 서비스의 메일이나 결제까지 이어지는 업무는 추가 계약을 요구한다.
분산 트랜잭션은 여러 참여자의 확정을 조정하고, 사가는 여러 단계와 보상 동작을 구성한다. 아웃박스는 업무 변경과 전달할 사건을 같은 DB에 기록한다. 이 선택들은 Gray가 구분했던 복구 범위를 서비스 아키텍처에서 다시 읽게 한다. 한 DB의 롤백이 담당하는 경계를 먼저 정해야 각 방식의 의미도 비교할 수 있다.
원자성을 보여주는 작은 롤백 예제
다음 PostgreSQL 예제는 재고 변경과 주문 기록이 같은 확정 범위에 있다는 점을 보여준다. 동시 주문 API를 구현하는 코드는 아니며, 두 쓰기를 함께 취소하는 의미를 확인한다.
CREATE TEMP TABLE stock (id integer PRIMARY KEY, quantity integer NOT NULL);
CREATE TEMP TABLE orders (id integer PRIMARY KEY, item_id integer NOT NULL);
INSERT INTO stock VALUES (1, 1);
BEGIN;
UPDATE stock SET quantity = quantity - 1 WHERE id = 1;
INSERT INTO orders VALUES (100, 1);
ROLLBACK;
SELECT quantity FROM stock WHERE id = 1;
SELECT count(*) FROM orders;
마지막 조회는 재고 1, 주문 수 0을 돌려준다. 두 변경을 함께 취소했기 때문이다. 같은 위치에서 COMMIT하면 두 변경을 확정한다.
각주
- K. P. Eswaran·J. N. Gray·R. A. Lorie·I. L. Traiger, 「The Notions of Consistency and Predicate Locks in a Database System」, CACM 19(11), 1976. 저자 기관 서지와 초록. ↩
- Jim Gray, 「The Transaction Concept: Virtues and Limitations」, VLDB, 1981. 저자 제공 원문. ↩
- Theo Härder·Andreas Reuter, 「Principles of Transaction-Oriented Database Recovery」, ACM Computing Surveys 15(4), 1983, pp.287–317. 원문 사본. ↩
- Hal Berenson 외, 「A Critique of ANSI SQL Isolation Levels」, SIGMOD, 1995, §4.2 및 A5B. 연구기관 원문. ↩
- Dan R. K. Ports·Kevin Grittner, 「Serializable Snapshot Isolation in PostgreSQL」, PVLDB 5(12), 2012, pp.1850–1861, §1–2.1.1. 출판기관 원문. ↩
- PostgreSQL 18, §13.2 “Transaction Isolation”. 공식 문서. ↩