−로그·체크포인트·변경을 저장 장치에 반영하는 전략은 이 성질들을 구현하는 방법으로 비교된다. 필요한 로그를 데이터 변경보다 먼저 보존하는 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의 롤백이 담당하는 경계를 먼저 정해야 각 방식의 의미도 비교할 수 있다.
+성질은 넷으로 정해져 있어도 구현 방법은 여럿이다. 로그와 체크포인트, 변경을 저장 장치에 반영하는 전략은 이 성질들을 구현하는 방법으로 비교된다. 필요한 로그를 데이터 변경보다 먼저 보존하라는 WAL 규칙은 복구의 순서를 설명하는 핵심이다.[각주 3] 일관성이 요구하는 업무 규칙은 스키마와 애플리케이션이 표현해야 한다. DB는 잘못 작성한 계산의 의도까지 알아내 주지 않는다.
1983년의 정리는 트랜잭션이 발명된 순간이라기보다 서로 다른 복구 구현을 같은 질문으로 설명할 용어가 자리 잡은 과정에 가깝다.
복구 범위와 외부 동작
Gray의 논문은 동작을 비보호(unprotected)·보호(protected)·실제(real) 동작의 세 부류로 나눠 복구 범위를 구분한다. 비보호 동작은 트랜잭션이 중단돼도 되돌리거나 다시 실행할 필요가 없고, DB 변경 같은 보호 동작은 되돌리거나 다시 실행할 수 있어야 한다. 현금 배출 같은 실제 동작은 한 번 일어나면 되돌릴 수 없으므로 커밋할 때까지 미룬다.[각주 2] 이 구분은 훗날 원격 서비스 호출이나 메시지 전달을 로컬 롤백과 어떻게 연결할지 이해하는 데도 쓰인다.
복구 시스템은 어떤 변경이 같은 작업에 속하고 무엇이 이미 확정됐는지 알아야 한다. 작업 경계와 커밋 기록이 제어 문법이면서 동시에 복구 판단의 근거가 되는 이유다.
동시 실행과 직렬가능성
여러 사용자가 같은 데이터를 다루면 작업 하나하나의 올바름과 전체 실행의 올바름이 갈라진다. 재고를 읽고 줄이는 절차는 따로 떼어 보면 맞아도, 두 작업이 서로의 읽기와 쓰기 사이에 끼어들면 결과가 달라진다.
Eswaran 등의 논문은 공유 데이터의 일관성과 실행 순서를 한 문제로 묶어, 어떤 잠금 규칙 아래에서 여러 작업을 섞어 실행해도 직렬 실행과 같은 결과를 얻는지 분석했다. 물리적인 레코드뿐 아니라 조건으로 지정한 데이터 집합을 보호하는 문제도 다뤘다.[각주 1] 이 논문에서 트랜잭션은 SQL 명령 하나의 이름보다 넓은 작업 단위이고, 뒤이은 복구와 격리 논의도 무엇을 한 작업으로 보고 어떤 실행을 허용할지라는 두 질문을 이어받는다.
직렬가능성은 성공한 작업들의 효과가 어떤 직렬 순서로 실행한 결과와 같아야 한다는 요구다. 실제 구현은 작업을 겹쳐 실행하면서, 충돌하는 작업을 기다리게 하거나 중단시켜 이 요구를 맞출 수 있다.
스냅샷 격리와 쓰기 왜곡
SQL의 격리 수준은 dirty read, nonrepeatable read, phantom 같은 이상 현상으로 설명돼 왔다. Berenson 등의 분석에서 드러나듯 안정적인 스냅샷을 읽는 것과 직렬가능성은 서로 다른 성질이다.[각주 4] 같은 시점의 데이터를 읽더라도 서로 다른 행을 쓰는 두 트랜잭션이 함께 규칙을 깨뜨릴 수 있는데, 이 쓰기 왜곡을 보면 “같은 행을 덮어쓰지 않았다”만으로는 동시 실행이 올바른지 판정할 수 없다.
Ports와 Grittner의 논문은 PostgreSQL 9.1의 SSI 구현이 제공하는 격리와 그 비용, 충돌 검사를 확인하는 근거로 쓰인다. 논문의 당직 예에서 두 트랜잭션은 각각 당직자 두 명을 읽은 뒤 자기 행만 근무 종료로 바꿀 수 있고, 스냅샷 격리에서는 쓰기 대상이 달라도 최종 당직자가 0명이 되는 실행이 가능하다. SSI는 읽기·쓰기 의존성을 추적해 직렬가능성을 깨뜨릴 수 있는 실행을 중단하는데, 거짓 양성 때문에 직렬가능한 실행이 중단될 수도 있다.[각주 5]
MVCC는 여러 버전을 관리하는 기법이고, 어느 버전을 읽고 어떤 충돌을 거절할지는 격리 규칙이 정한다. PostgreSQL 18에서도 Read Committed·Repeatable Read·Serializable의 관찰 결과는 서로 다르며, 직렬화 실패가 나면 호출자가 실패한 트랜잭션 전체를 다시 판단해야 한다.[각주 6]
서비스 간 업무
트랜잭션을 적용할 범위가 여러 서비스로 넓어지면 DB의 작업 단위와 사용자에게 보이는 업무의 길이가 달라진다. 한 DB 안의 주문과 재고는 로컬 트랜잭션에 넣을 수 있지만, 다른 서비스의 메일이나 결제까지 이어지는 업무에는 추가 계약이 필요하다.
분산 트랜잭션은 여러 참여자의 확정을 조정하고, 사가는 여러 단계와 보상 동작을 구성한다. 아웃박스는 업무 변경과 전달할 사건을 같은 DB에 같은 로컬 트랜잭션으로 기록한다. 이 방식들에서 Gray가 구분한 복구 범위의 문제를 서비스 아키텍처의 맥락으로 다시 읽을 수 있다. 각 방식의 의미를 비교하려면 한 DB의 롤백이 어디까지 책임지는지부터 정해야 한다.