데이터베이스 따라잡기

분산 트랜잭션 개요

meellon 2026. 7. 3. 05:30

학습 목표

분산 트랜잭션여러 DB·서비스에 걸친 원자성 문제를 만든다는 것을 설명할 수 있다.

2PC(Two-Phase Commit) 의 prepare·commit 단계와 블로킹·조정자 SPOF 한계를 설명할 수 있다.

3PC가 2PC를 개선하려다 복잡도만 늘리는 이유를 설명할 수 있다.

Saga(오케스트레이션·코레오그래피)와 보상 트랜잭션으로 ** eventual consistency**를 설계할 수 있다.

Transactional outboxDB COMMIT이벤트 발행한 로컬 TX에 묶는 이유를 설명할 수 있다.

문제 상황

  • 주문 DB COMMIT 후 결제 API 타임아웃 — 주문만 생성
    • 두 시스템 하나의 ROLLBACK 불가
  • 샤드 A·B에 한 번에 INSERT — cross-shard ACID 기대
    • PostgreSQL 한 인스턴스 밖은 기본 2PC 없음
  • XA 2PC 도입 — coordinator 다운 시 participant prepare 상태 고착
  • 결제 취소 API 없이 saga 보상 설계 — 돈만 빠짐
  • UPDATE orders + kafka.send — DB 커밋 후 MQ 장애이벤트 유실

앞서 ACID한 DBMS 안이었다. 샤딩·MSA로컬 TX보장, 전역 원자성은 별도 패턴.

1. 분산 트랜잭션이란

분산 트랜잭션두 개 이상독립 리소스(DB·큐·외부 API)에 걸친 작업을 하나의 단위처럼 다루려는 시도.

  로컬 TX 분산 TX
범위 DB connection 여러 노드·서비스
ACID 엔진이 보장 프로토콜·패턴 필요
실패 ROLLBACK 한 번 부분 커밋·보상
BEGIN … COMMIT 2PC, saga, outbox
Goal:  all succeed  OR  all undo
Reality:  network · partial failure · no global lock
  • 앞서 샤딩 — cross-shard JOIN·TX 비용설계로 줄임
  • CAP분할강한 일관성 vs 가용성 — Architecture 일관성 모델과 연결

2. Two-Phase Commit (2PC)

2PC조정자(coordinator) 가 participant에게 두 단계전원 commit/abort 합의.

Phase 1 — Prepare

Coordinator  →  each participant:  PREPARE
Participant  →  write undo/redo, lock resources, vote YES/NO

Phase 2 — Commit / Abort

All YES  →  COMMIT (each participant)
Any NO or timeout  →  ABORT (rollback)
역할  
Coordinator 트랜잭션 시작·결정
Participant prepare 투표 · commit/abort 실행
-- XA-style (conceptual — rare in app code)
-- xa start 'xid';
-- UPDATE account_a ...
-- xa end 'xid'; xa prepare 'xid';
-- (coordinator collects votes)
-- xa commit 'xid';  or xa rollback 'xid';

2PC 한계

문제  
Blocking coordinator 죽으면 participant prepare 상태 잠금
SPOF coordinator 가용성
성능 2 RTT + disk sync · 잠금 장기
운영 heuristic commit·in-doubt 복구 수동
MSA 서비스 경계마다 2PC — 결합·지연
  • PostgreSQLprepared transaction드물게 쓰임, 모니터링 필요
  • 실무 MSA — 2PC/XA 회피saga·outbox

3. Three-Phase Commit (3PC)

3PC — 2PC blocking 완화 시도 — CanCommit · PreCommit · DoCommit (교재·이론).

  2PC 3PC
단계 2 3
blocking coordinator fail → block 완전 해소 아님
네트워크 분할 in-doubt non-blocking 주장한계
실무 XA, DB 내부 거의 미사용
  • 결론강한 분산 원자성비용 큼 — 피하고 saga·idempotency

4. Saga

Saga로컬 TX 체인 + 실패 시 보상(compensating) TX — 전역 rollback 대신.

  Orchestration Choreography
조율 중앙 saga orchestrator 이벤트분산
흐름 명령 순서 명시 OrderPlacedReserveStock
가시성 한 곳 상태 흩어진 consumer
적합 복잡 워크플로 단순 이벤트 체인
Happy path:
  T1: create order     (local COMMIT)
  T2: charge payment   (local COMMIT)
  T3: reserve stock    (local COMMIT)

Failure at T3:
  C2: refund payment   (compensate)
  C1: cancel order     (compensate)
# code/saga-compensation.example (orchestrator sketch)
steps = [
    ("create_order", "cancel_order"),
    ("charge_payment", "refund_payment"),
    ("reserve_stock", "release_stock"),
]
for forward, compensate in steps:
    try:
        run(forward, ctx)
    except Exception:
        for done_forward, done_comp in reversed(completed):
            run(done_comp, ctx)
        raise
    completed.append((forward, compensate))
설계  
보상 비즈니스 취소 — DELETE가 아님 반대 거래
멱등 compensate 재시도 safe
순서 역순 보상
격리 중간 상태 노출 — UI pending
  • Architecture 동기·비동기에서 saga 흐름 — DB 관점은 각 step = 로컬 TX

5. Transactional Outbox

Outbox비즈니스 row + outbox event row같은 DB TXCOMMIT별도 publisher가 MQ 전송.

-- code/outbox-table.sql
BEGIN;
INSERT INTO orders (id, user_id, amount) VALUES (1, 42, 9900);
INSERT INTO outbox (id, aggregate_type, aggregate_id, payload, created_at)
VALUES (
  gen_random_uuid(),
  'Order',
  '1',
  '{"event":"OrderPlaced","orderId":1}',
  now()
);
COMMIT;
-- poller / Debezium reads outbox → publishes to Kafka
  Dual write (bad) Outbox
DB + MQ 두 번 쓰기 — 하나 실패 한 TX
일관성 유실·중복 at-least-once + idempotent consumer
CDC Debezium outbox router
Same DB transaction:
  orders row  +  outbox row  →  COMMIT
Async:
  publisher  →  message bus  →  saga next step
  • Inbox — consumer 중복 처리 — processed_messages 테이블
  • Orderingpartition key = aggregate_id

6. 선택 가이드

상황 권장
한 DB 로컬 TX기본
Cross-shard avoid샤드 키 설계 · aggregate per shard
MSA 2~3 서비스 Saga + outbox
강한 전역 ACID 한 DB모델 재설계 — 2PC 최후
읽기 ** eventual OK** — lag 허용 (앞서 복제)
안티패턴  
분산 2PC everywhere blocking·ops
보상 없는 saga 돈·재고 불일치
MQ 먼저 send DB rollback유령 이벤트
Long saga 중간 상태 hours타임아웃·만료

7. 체크리스트

항목 확인
경계 한 aggregate = 한 shard?
실패 각 step compensate 정의?
Outbox business + event same TX?
Consumer idempotent?
2PC 정말 필요한지 — ADR
Monitor prepared txn · outbox lag

8. 정리

  • 분산 TX로컬 ACID 전역 ACID
  • 2PC — prepare/commit — blocking·SPOF — MSA 드묾
  • 3PC — 이론적 — 실무 거의 없음
  • Saga로컬 TX + 보상 — orchestration vs choreography
  • OutboxCOMMIT이벤트 한 TX — saga·CDC 연결

다음에 다룰 것

  • NoSQL 개요
  • KV·문서·CAP, 워크로드별 선택

해당 내용은 Database System Concepts, 7/E (Avraham Silberschatz, Henry F. Korth, S. Sudarshan), Designing Data-Intensive Applications (Martin Kleppmann) 의 내용을 기반으로 합니다.

'데이터베이스 따라잡기' 카테고리의 다른 글

Connection Pool과 ORM  (0) 2026.07.05
NoSQL 개요  (0) 2026.07.04
파티셔닝과 샤딩  (0) 2026.07.02
복제  (0) 2026.07.01
MVCC  (0) 2026.06.30