コンテンツにスキップ

分散トランザクションパターンまとめ

用途: マイクロサービス / 複数DB / 複数サービス間で整合性を保つための設計パターン。面接深掘り対策と実務メモを兼ねる。


1. なぜ分散トランザクションが問題になるか

単一DB内のトランザクション (ローカルトランザクション) は ACID で守られる。 しかし、以下のいずれかが発生すると ACID が単純には効かない:

  • 複数DB (例: マッチング結果 → 残高DB + 台帳DB + 通知系)
  • 複数サービス (例: 注文サービス → 残高サービス → 清算サービス)
  • 外部システム連携 (例: カバー取引で外部LP API呼び出し)

このとき、A の更新は成功したが B が失敗した という部分的失敗 (partial failure) が起こる。 これを整合的に扱うのが分散トランザクションパターンの目的。


2. 主要パターン早見表

パターン 整合性 レイテンシ 実装難度 主な用途
2PC (Two-Phase Commit) 強整合 低速 (ブロッキング) 同一信頼境界内の複数RDB
Saga (Orchestration) 結果整合 中速 中〜高 長期ワークフロー、補償可能な処理
Saga (Choreography) 結果整合 中速 高 (追跡困難) 疎結合な小規模サービス連携
TCC (Try-Confirm-Cancel) 結果整合 (短時間) 中速 在庫予約・席予約など短期ロック型
Outbox Pattern 結果整合 中速 低〜中 DB更新 + メッセージ送信のアトミック化
Event Sourcing + CQRS 結果整合 中速 監査ログ・状態復元・ドメインイベント

3. 2PC (Two-Phase Commit)

概要

調停者 (Coordinator) がすべての参加者に「準備OK?」を聞き、全員OKなら「コミット」、誰かNGなら「ロールバック」を指示する。

Phase 1 (Prepare):
  Coordinator → A: PREPARE → A: 「OK」 (ロック取得、ログ書き込み)
  Coordinator → B: PREPARE → B: 「OK」
  Coordinator → C: PREPARE → C: 「OK」

Phase 2 (Commit):
  Coordinator → A: COMMIT → A: 「Done」
  Coordinator → B: COMMIT → B: 「Done」
  Coordinator → C: COMMIT → C: 「Done」

保証

  • 強整合性: ACID を分散環境でも維持
  • アトミック: 全員成功 or 全員失敗

問題点

  1. ブロッキング: Phase 1 でロック取得 → Phase 2 完了まで他トランザクションを待たせる
  2. Coordinator 単一障害点 (SPOF): Coordinator が Phase 2 の途中で落ちると参加者が宙ぶらりんに
  3. ネットワーク分断に弱い: Phase 2 のメッセージが届かないと参加者が判断できない
  4. パフォーマンス: 2ラウンドトリップ + ロック時間が長い → 高スループット領域では非現実的

いつ使うか

  • 同一データセンター内の複数RDB
  • XA トランザクションをサポートするRDB (PostgreSQL, MySQL の XA, Oracle)
  • スループットよりも整合性が圧倒的に優先される少数のシナリオ

いつ使わないか

  • マイクロサービス間のトランザクション (推奨されない)
  • 外部API呼び出しを含む処理 (外部APIが XA 対応していない)
  • レイテンシ要求が厳しい処理 (取引マッチングなど)

4. Saga パターン

概要

長期トランザクションを複数のローカルトランザクションに分割し、失敗時は補償トランザクション (Compensating Transaction) で取り消す。

正常系:
  T1: 注文作成 → T2: 残高引落 → T3: 在庫確保 → T4: 配送指示

T3 で失敗したら:
  C2: 残高戻し ← C1: 注文取消

バリエーション

(a) Orchestration (オーケストレーション)

中央の Orchestrator が各ステップを順に呼ぶ。

Orchestrator → T1 → T2 → T3 (失敗)
           → C2 → C1
  • 長所: フローが1箇所に集約され、追跡しやすい / 状態管理が容易
  • 短所: Orchestrator が中央集権的になる / Orchestrator のロジックが肥大化しやすい

(b) Choreography (コレオグラフィー)

各サービスがイベントを発行し、他サービスがそれに反応して動く。

注文サービス → "OrderCreated" イベント
              残高サービス: 引落 → "BalanceDeducted" イベント
                                在庫サービス: 確保 → 失敗 → "InventoryFailed"
                                                          残高サービス: 戻し
                                                          注文サービス: 取消
  • 長所: サービス間が疎結合 / 中央点なし
  • 短所: フロー全体の追跡が困難 / デバッグしづらい / 暗黙の依存関係が増える

補償トランザクション (Compensating Transaction) の設計

  • 必須要件: ビジネスロジックで「巻き戻し可能」であること
  • 冪等性 (Idempotency) が重要 — 同じ補償を複数回実行しても結果が変わらない
  • 意味的補償: 「キャンセル」「返金」「在庫戻し」 — 完全に元に戻すのではなく、ビジネス的に等価な状態に持っていく
  • 例: メール送信は取り消せないので、「お詫びメール送信」で補償
  • 例: 印刷ジョブは取り消せないので、「破棄指示」で補償

問題点

  1. 結果整合性のみ — 一時的に不整合な状態を許容する必要がある
  2. 隔離性なし — 他のトランザクションが中間状態を読む可能性 → アンチパターン回避策が必要
  3. 補償できないステップ — 完全に巻き戻せない処理 (外部送金、通知送信) は意味的補償で対応
  4. Saga の途中失敗時の追跡 — Orchestrator か Saga Log で状態管理必須

隔離性問題の回避策

  • Semantic Lock: 中間状態に「pending」フラグを付け、他トランザクションから見えないようにする
  • Commutative Updates: 順序に依存しない更新 (incrementなど) を使う
  • Pessimistic View: 結果が確定するまで読まれないようにする
  • Reread Value: 補償時に値を再読み込みして整合性チェック

いつ使うか

  • マイクロサービス間の長期トランザクション
  • 外部API呼び出しを含むワークフロー
  • enechain の Process Manager パターンはこれに該当

5. TCC (Try-Confirm-Cancel)

概要

Saga の特殊形。各ステップが Try / Confirm / Cancel の3操作をサポートする。

Try Phase: 全サービスでリソースを予約 (まだ確定しない)
  注文サービス.try() → 仮注文作成
  残高サービス.try() → 残高を「予約済み」状態に
  在庫サービス.try() → 在庫を「確保中」状態に

Confirm Phase (全 Try 成功):
  全サービスで Confirm → 予約を確定

Cancel Phase (どれか Try 失敗):
  全サービスで Cancel → 予約解放

2PC との違い

  • 2PC: DB レベルのロック
  • TCC: アプリケーションレベルでの予約状態

長所

  • DB ロックを長時間掴まない (2PC より高スループット)
  • 補償ロジックが「Cancel」として明示的にあるため Saga より分かりやすい

短所

  • 各サービスに Try / Confirm / Cancel の3操作を実装する必要がある
  • アプリケーションロジックが複雑化
  • Try 状態のタイムアウト管理が必要

いつ使うか

  • 在庫予約、座席予約、リソース確保系
  • 短期間で確定/破棄が決まる処理

6. Outbox Pattern

概要

「DB更新 + メッセージ送信」をアトミックに実現するパターン。

従来のNG例:
  1. DBに注文を書き込む
  2. メッセージキューに「OrderCreated」を送る
  → 1は成功したが2が失敗すると、メッセージが永久に失われる

Outbox Pattern:
  1. 同一トランザクションで:
     - orders テーブルに注文を書き込む
     - outbox テーブルに「OrderCreated」イベントを書き込む
  2. 別プロセス (Relay/Polling) が outbox を読んで MQ に送信
  3. 送信成功後、outbox から削除 (or flag更新)

保証

  • DB更新とイベント発行のアトミック性
  • At-least-once 配信 (重複あり得るので消費側で冪等性必要)

実装方式

(a) Polling Publisher

  • 別プロセスが定期的に outbox テーブルを SELECT してメッセージ送信
  • シンプルだが、ポーリング間隔の分のレイテンシ

(b) Transaction Log Tailing (CDC)

  • Debezium / Datastream で DB の WAL/binlog を読み、変更を MQ に流す
  • レイテンシ低 / アプリケーション側の改修不要
  • enechain も Datastream で CDC を使っている (テックブログ記載)

いつ使うか

  • マイクロサービス × イベント駆動アーキテクチャ
  • 「DB更新したらイベントを必ず発行したい」場面すべて

7. Event Sourcing + CQRS

概要

状態をイベントの追記ログとして保存し、現在状態はイベントを再生して構築する。

従来:
  accounts テーブル: id=1, balance=10000

Event Sourcing:
  events テーブル:
    1. AccountCreated(id=1, initial=0)
    2. Deposited(id=1, amount=15000)
    3. Withdrawn(id=1, amount=5000)
  → 再生すると balance=10000

保証

  • 任意時点の状態復元: 監査要件・取引ログ保全に直結
  • イベントは Immutable: 追記のみ、更新削除なし

CQRS との組み合わせ

  • Command 側: イベントを書く (Event Store)
  • Query 側: イベントから構築した Read Model を別DBに保持
  • 読み書きの責務を分離 → 高スループット & 強監査性

問題点

  1. イベントスキーマ進化 — イベントは Immutable なので、過去イベントを後から変更できない → versioning が必要
  2. プロジェクション再構築 — Read Model を作り直すのに時間がかかる (イベント全再生)
  3. スナップショット戦略 — 全イベント再生は重いので、定期的にスナップショットを取る
  4. 学習コスト — チームに浸透するまで時間がかかる

いつ使うか

  • 監査要件・状態復元要件が強い (金融・取引・規制産業)
  • イベント駆動アーキテクチャの中核
  • enechain の eClear / eSquare で採用

8. 冪等性 (Idempotency) の重要性

分散トランザクションのほぼ全パターンで冪等性が前提になる。

冪等性とは

  • 同じ操作を複数回実行しても結果が変わらない性質
  • 例: set balance = 100 は冪等 / balance += 100 は非冪等

実装パターン

(a) Idempotency Key

  • リクエストに一意のキーを含める
  • サーバー側で「このキーは既に処理済み」を記録 → 同じキーの再送は無視 or 同じ結果を返す
  • Stripe API などが採用

(b) Versioning / Optimistic Locking

  • 楽観ロック: WHERE version = ? で更新失敗を検知

(c) State Machine

  • 状態遷移を明示的にし、同じ遷移は1回しか起きないようにする

9. パターン選択フロー

Q1. 同一信頼境界内 (同じチーム・同じデータセンター・短期) か?
  YES → 2PC (XAトランザクション) を検討
  NO → 次へ

Q2. 全ステップがビジネス的に補償可能か?
  YES → Saga を検討
  NO → 補償できないステップを最後に置く / 設計見直し

Q3. ステップが「予約 → 確定/解放」型か?
  YES → TCC を検討
  NO → Saga

Q4. DB更新 + イベント発行のアトミック性が欲しいだけ?
  YES → Outbox Pattern (単独で使う)
  NO → 上記と組み合わせ

Q5. 監査要件・任意時点状態復元が必要か?
  YES → Event Sourcing + CQRS を検討
  NO → 通常のSagaやOutboxで十分

10. コインチェック・enechain 文脈での整理

コインチェックでの実例 (本人経験)

処理 採用パターン
マッチング → 残高更新 → 台帳記帳 同一DBトランザクション (モノリス内) で完結。マイクロサービス化していない
取引ログ・約定ログ 性能経路と監査経路の 別系統保全 (=実質的に Outbox 的発想)
カバー取引 (外部LP呼出含む) Aggregator経由でラップ。失敗時は手動 or 業務フローで補償 (= 簡易 Saga)

enechain での想定 (テックブログから推測)

処理 採用パターン
eSquare 注文掲載プロセス Process Manager (Saga Orchestration の体系的実装)
清算・決済 (eClear) Event Sourcing + CQRS
DB更新 → 通知配信 PostgreSQL LISTEN/NOTIFY (Outbox の軽量版に近い)
BigQuery集計 Datastream (CDC) = Transaction Log Tailing 方式の Outbox

11. 面接で語る時のスタンス

Why層で評価される回答の型

  1. 「銀の弾丸はない」を最初に言う — マイクロサービスもSagaも万能ではない
  2. トレードオフを明示する — 整合性 vs スループット vs 実装複雑度
  3. ドメイン制約から導く — 「金融取引は監査要件があるので Saga の中間状態は許容できない」など
  4. 既存アーキとの接続 — 「コインチェックではモノリスで強整合を維持した」「enechain は Process Manager で長期ワークフローを分離している」と具体例で語る

一次面接で深掘りされた時の応答ストック

質問 回答の核
「マイクロサービス化したらどんな問題?」 強整合の喪失 / レイテンシ増 / 順序保証困難 / Saga中間状態の監査リスク
「Sagaで補償できないステップは?」 意味的補償 (お詫びメール、破棄指示) で代替する
「2PCはなぜ使わない?」 ブロッキング / SPOF / マイクロサービス連携で非現実的
「Event Sourcing の運用課題は?」 スキーマ進化 / プロジェクション再構築時間 / スナップショット戦略
「冪等性をどう担保?」 Idempotency Key / Versioning / State Machine

Sources / 参考

  • Chris Richardson『マイクロサービスパターン』
  • Martin Fowler "Saga" (martinfowler.com)
  • enechain TechBlog: Process Manager パターン記事 / LISTEN-NOTIFY 記事
  • AWS Database Blog: Saga / TCC 解説
  • Microsoft Cloud Design Patterns: Compensating Transaction, Outbox

最終更新: 2026-05-12


作成: 2026-05-12 / 最終更新: 2026-06-16