−1. 기록을 복구하는 문제에서 작업 단위로
처리가 여러 번의 저장으로 이루어지면 장애가 발생한 지점에 따라 일부 변경만 남을 수 있다. 복구 시스템에는 어느 변경들이 같은 작업에 속하고, 어디까지 성공으로 받아들였는지를 알아낼 정보가 필요하다. 트랜잭션 경계와 커밋 기록은 이 판단의 기준이 된다.
1976년 Eswaran·Gray·Lorie·Traiger의 논문은 트랜잭션과 실행 순서, 일관성과 잠금의 관계를 형식화했다. 여러 사용자가 공유 데이터를 동시에 다루는 상황에서, 개별 작업의 올바름을 어떤 실행 규칙으로 유지할지를 다룬 연구다.[각주 1]
Jim Gray의 1981년 「The Transaction Concept: Virtues and Limitations」는 트랜잭션을 상태의 변환으로 설명하며 원자성·일관성·내구성과 적용 범위를 논의했다. 논문은 DB 안에서 보호되는 동작과 외부 세계에서 일어난 동작을 구별한다. 변경 기록을 되돌리는 일과 실제로 배출한 현금을 회수하는 일의 차이처럼, 복구의 경계를 개념의 일부로 보았다.[각주 2]
2. 1983년 ACID가 정리한 성질
Theo Härder와 Andreas Reuter는 1983년 데이터베이스 복구 논문에서 네 성질을 ACID라는 이름으로 묶어 설명했다. 이는 앞서 발전하던 트랜잭션 개념의 성질을 정리한 기록이다.[각주 3]
성질 | 묻는 내용 | 이체 예제에서의 의미
Atomicity, 원자성 | 작업의 일부만 확정되는가 | 출금과 입금을 함께 확정하거나 함께 취소한다
Consistency, 일관성 | 작업이 정한 무결성 규칙을 유지하는가 | 금액과 잔액에 관한 규칙을 만족하도록 변환한다
Isolation, 격리성 | 함께 실행되는 작업이 어떻게 영향을 주는가 | 중간 상태와 충돌을 어떤 실행 규칙으로 다룬다
Durability, 내구성 | 확정한 결과가 장애 뒤에도 남는가 | 커밋한 이체 기록을 저장·복구 계약에 따라 유지한다
일관성의 규칙에는 DB의 제약 조건뿐 아니라 애플리케이션이 구현한 업무 조건도 포함된다. 트랜잭션은 잘못 작성한 금액 계산의 의도를 알아내지 못한다. 어떤 상태가 올바른지는 스키마와 프로그램이 표현해야 한다.
3. 커밋과 복구는 어떻게 연결되는가
대표적인 구현에서는 로그에 변경과 확정 여부를 남기고, 장애 뒤 그 기록을 사용해 커밋한 작업을 복원하거나 미완료 작업을 처리한다. write-ahead logging은 데이터 페이지보다 먼저 필요한 로그를 보존하는 규칙을 중심에 둔다. 체크포인트는 복구할 때 어느 지점부터 살펴볼지 줄이는 데 사용된다.[각주 3]
로그가 있다고 해서 모든 제품이 같은 복구 알고리즘이나 내구성 설정을 사용하는 것은 아니다. 중요한 연결은, 성공 응답을 어느 시점의 기록 보존과 결부시키느냐다. 복구 전략과 저장 장치의 계약이 내구성을 구체화한다.
4. 격리 수준은 왜 여러 개인가
겹친 작업을 전부 직렬로 실행하면 이해하기는 쉽지만, 독립적인 작업도 기다리게 된다. 데이터베이스는 더 많은 작업을 함께 처리하면서 허용할 관찰 결과를 구분해 왔다. SQL의 격리 수준은 dirty read, nonrepeatable read, phantom 같은 이상 현상과 연결해 설명된다.
Berenson 등의 1995년 「A Critique of ANSI SQL Isolation Levels」는 당시 정의의 모호함을 분석하고, 스냅샷 격리와 쓰기 왜곡(write skew)을 다뤘다. 같은 스냅샷을 읽는다는 사실과 전체 실행이 어떤 직렬 순서와 같다는 성질 사이의 차이를 보여준 연구다.[각주 4]
예를 들어 Alice와 Bob이 모두 당직 중이고 최소 한 명이 남아야 한다고 하자. 두 트랜잭션이 각각 당직자가 두 명이라고 읽고 자신의 행만 근무 종료로 바꾸면, 같은 행을 덮어쓰지 않았어도 최종 당직자는 0명이 된다. 한 작업씩 끝냈다면 두 번째 작업은 남은 사람이 자신뿐임을 보았을 것이다.
직렬가능성(serializability)은 성공한 작업들의 효과가 어떤 직렬 실행 순서와 같아야 한다는 요구다. 실제 구현은 작업을 겹쳐 실행하면서 충돌하는 경우를 대기시키거나 중단해 이 요구를 맞출 수 있다.
5. MVCC는 격리를 구현하는 재료다
다중 버전 동시성 제어(MVCC)는 여러 버전의 데이터를 유지해 읽는 작업이 적절한 버전을 선택하도록 한다. 어떤 시점의 버전을 읽고 어떤 의존성 충돌을 허용하는지는 격리 규칙이 정한다.
PostgreSQL 18의 일반 SELECT에서는 Read Committed가 문장마다 스냅샷을 얻고, Repeatable Read가 트랜잭션에서 유지하는 스냅샷을 사용한다. 이 제품의 Repeatable Read는 팬텀을 방지하지만 쓰기 왜곡 같은 직렬화 이상을 허용할 수 있다. Serializable은 직렬화할 수 없는 실행을 중단할 수 있으므로 호출자에게 실패한 작업을 처음부터 다시 판단하는 처리가 필요하다.[각주 5]
이 사례에서 구분할 축은 데이터 버전의 보관, 읽을 버전의 선택, 충돌 검출, 중단 이후의 처리다. MVCC라는 이름 하나에 이 결정들이 모두 들어 있지는 않다.
6. 가장 작은 롤백 예제
다음 PostgreSQL 예제는 주문 기록과 재고 변경의 확정 범위를 보여준다. 동시 주문 처리를 구현하려는 코드가 아니라, 두 쓰기를 같은 작업으로 묶는 의미를 확인하기 위한 예제다.
+공유 데이터의 일관성을 실행 순서와 연결하다 (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, 내구성 | 커밋된 결과를 저장·복구 계약에 따라 유지한다
로그·체크포인트·변경을 저장 장치에 반영하는 전략은 이 성질들을 구현하는 방법으로 비교된다. 필요한 로그를 데이터 변경보다 먼저 보존하는 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를 구현하는 코드는 아니며, 두 쓰기를 함께 취소하는 의미를 확인한다.