📝ノート💻テック

分散トランザクション解説:2 Phase Commit vs Saga Pattern

Tony Duong

Tony Duong

6月 2, 20261

他の言語:🇫🇷🇬🇧
#system-design#distributed-systems#saga#two-phase-commit#microservices#interview
分散トランザクション解説:2 Phase Commit vs Saga Pattern

Hello Interviewによる分散トランザクションのメモ — 1つのDBが複数になると何が変わるか、two-phase commit(2PC)がサービス間でほとんど使われない理由、業界がSagaを選ぶ理由。

トランザクションが簡単だった頃

初期は単一DBがすべてを処理。注文時:カード課金、在庫確保、会計仕訳 — すべて1トランザクション。失敗すればDBが自動ロールバック。

ここで重要なACID保証:

  • 原子性 — 3つの書き込みがすべて成功するか、どれもしない
  • 隔離性 — トランザクション中に途中状態を他のクエリが見ない

スケールアウトで変わること

トラフィックとデータが増える。DBをシャードするかマイクロサービス化 — 各サービスが独自のDBを持つ。

1トランザクションだった決済フローが3つの独立DBへの別々の操作になる:

  1. カード課金(決済DB)
  2. 在庫確保(在庫DB)
  3. 仕訳記録(会計DB)

独立DB間にトランザクションを張れない。課金がコミットした後に在庫確保が失敗しても、DBレベルのロールバックはない。

これが分散トランザクション:複数の独立DB/サービスにまたがる1つの論理操作で、すべて成功するかクリーンアップが必要。

Two-phase commit(2PC)

古典的な学術的解。コーディネータが全参加者の合意を確認してから確定させる。

Phase 1 — Prepare

各参加者に「コミットできるか?」と聞く。各DBが作業し、変更を永続化し、行をロックし、yes/noで投票。

  • no が1つでも → 全員abort
  • 全員yes → Phase 2へ

Phase 2 — Commit

全員にcommitを送信。参加者が変更を確定しロック解除。

利点: 強い一貫性 — 単一DBと同じ保証。

2PCが本番で失敗する理由

2PCはブロッキングプロトコル — 複数マシンが同時に健全であることに依存するため危険。

コーディネータクラッシュ: 全yesを集めた後、commit送信前にクラッシュ。参加者はロックを保持したまま動けない。それらの行に触れるトランザクションがすべてブロック。

業界の現実: 独立サービス間で2PCを使う者はほぼいない。Pat HellandのLife Beyond Distributed Transactionsは、自律サービス間の分散トランザクションはインターネット規模では機能しないと論じる。

2PCは分散DB内部(Google Spanner、YugabyteDB)では使われる — コーディネータと参加者が密結合。独立サービス間では破綻する。

Sagaパターン

Uber、Netflix、Amazon、DoorDashが本番で使うもの。

別の前提: サービス間のall-or-nothing原子性は不要。最終的に一貫した状態に到達できればよい。

  • 独立したローカルトランザクションの連鎖に分割
  • 各サービスが独自のDBにコミット
  • 後続ステップ失敗時は補償アクション(ビジネスレベルの取り消し)

トレードオフ: 結果整合性。補償中は一時的に不整合(課金のあと返金が見える)が、他のトランザクションはブロックされない。

コレオグラフィ vs オーケストレーション

コレオグラフィ(分散)

各サービスが完了時にイベントを発行。他が反応。

2–3ステップの単純フロー向き。5–6サービスでは状態追跡が困難。

オーケストレーション(集中)

オーケストレータがステップごとに制御。失敗時に補償の順序を把握。

ツール:TemporalAWS Step Functions

2PCコーディネータとの違い: オーケストレータは耐久。クラッシュ後、DBから状態を読み再開 — ダングリングロックなし。

大規模チームの多くはオーケストレーションを使う。

補償アクション — 難しい部分

  • 返金は顧客に可視
  • 取り消せない操作:送信済みメール、第三者webhook
  • 補償も失敗する → リトライ、冪等が必須

デュアルライト問題とトランザクションアウトボックス

DB保存とイベント公開は別システムへの2書き込み。

修正:トランザクションアウトボックス

  • データとイベントを同一DBの1ローカルトランザクションで書く(outboxテーブル)
  • バックグラウンドプロセス(CDCまたはポーリング)がブローカーへ公開

意思決定フレームワーク

まず:分散トランザクションは本当に必要か?

一緒にトランザクションするデータを同一DBに置けるならそうする。ローカルACIDが最善。

どうしても分散が必要なら

Saga — 業界のデフォルト。

状況 選択
3–4ステップ、中央可視性不要 コレオグラフィ
複雑フロー、補償が難しい オーケストレーション
結果整合性が不可 単一分散DB(Spanner等)— サービス間DIY 2PCではない

本番パターン

オーケストレーションSaga + 各ステップの冪等性 + トランザクションアウトボックス

要点

  • サービスごとにDBを分けると単一DBのACIDは崩れる
  • 2PCは強い一貫性だがブロックする
  • Sagaはローカルコミット + 補償
  • トランザクションアウトボックスでデュアルライトを回避
  • 可能なら:トランザクションを分散しない

🌐 Claudeによる翻訳

Tony Duong

著者: Tony Duong

デジタル日記。思考、経験、そして人生についての考え。