2相コミット(2PC)と分散トランザクション
複数DBにまたがる更新を、全部成功か全部取消にそろえる仕組みがわかります。prepare/commit の2段階と、なぜブロッキングが起きるのかを原理から押さえます。
- 1.2PC はコーディネータが全参加者に prepare で投票を募り、全員 YES なら commit、1人でも NO なら abort して原子性をそろえる。
- 2.prepare で YES を返した参加者はコミットもアボートもできず待つしかないため、コーディネータ障害でロックを抱えたまま固まるブロッキング問題がある。
- 3.3PC は pre-commit 段階を挟んでブロッキングを緩和するが、ネットワーク分断には弱く、実務では Saga や Paxos/Raft 系合意が選ばれることが多い。
分散トランザクションが難しい理由
単一DB内なら、トランザクションは WAL(先行書き込みログ)と1つのコミットレコードで原子的に確定できます。ところが更新対象が複数のDBやサービスにまたがると、各ノードはそれぞれ独立にコミット/ロールバックを判断するため、「Aだけ成功してBは失敗」という中途半端な状態が起こりえます。これではACID の原子性(Atomicity)が崩れます。
この「全ノードを一斉に、全部成功か全部取消へそろえる」ための古典的合意プロトコルが 2相コミット(Two-Phase Commit, 2PC) です。1人の コーディネータ(調整役) が、複数の 参加者(各DBノード) の判断を取りまとめます。
prepare/commit の2フェーズ
名前のとおり、確定までを2段階に分けます。
フェーズ1: 準備(prepare / voting)
コーディネータが全参加者に prepare(投票要求)を送ります。各参加者は自分のローカル更新を実行し、コミット可能かを判断します。
- 可能なら、更新内容と「コミットすると約束した」事実を永続ログに書いてから
YES(vote-commit)を返す。 - 制約違反やディスク不足などで無理なら
NO(vote-abort)を返す。
ここが核心です。YES を返した参加者は、ロックを保持したまま自分ではもう結末を決められない「準備済み(prepared / in-doubt)」状態に入ります。あとはコーディネータの最終指示に従う義務を負います。
フェーズ2: 確定(commit / abort)
コーディネータは投票を集計します。
- 全員 YES なら、自分の決定をログに書いてから全員に
commitを送る。 - 1人でも NO(または応答なし)なら、全員に
abortを送る。
各参加者は指示どおりにローカルを確定し、ACK を返します。全 ACK が揃えばトランザクション完了です。
Coordinator Participant A Participant B
|---- prepare ---------->| |
|---- prepare ------------------------------->|
|<--- YES ---------------| |
|<--- YES -------------------------------------|
| (全員YES → commit決定をログに記録) |
|---- commit ----------->| |
|---- commit ------------------------------->|
|<--- ACK ---------------| |
|<--- ACK -------------------------------------|
コーディネータが commit/abort の決定を自分のログに永続化してから指示を送るのは、途中で落ちても再起動後に同じ決定を再送(リカバリ)できるようにするためです。参加者側も prepare 時に YES の事実をログに残すので、再起動後にコーディネータへ「結末はどうなった?」と問い合わせ(in-doubt 解決)できます。ログを書く順序が、原子性の保証そのものです。
ブロッキング問題
2PC の最大の弱点がブロッキングです。フェーズ1で YES を返した直後、フェーズ2の指示が届く前にコーディネータが落ちると何が起きるか。
準備済みの参加者は、自分の判断ではコミットもアボートもできません(勝手にコミットすれば、他参加者がアボートしていたとき不整合になる)。だからロックを握ったまま、コーディネータの復帰をひたすら待つしかありません。この間、対象データは他のトランザクションから触れず、最悪システム全体が固まります。
「コーディネータが落ちたなら、生きている参加者だけで多数決すればいい」とはいきません。落ちた参加者がコーディネータからすでに commit を受け取っていた可能性があり、その事実を生存者は知りえないからです。結末を一意に決められる情報がコーディネータのログにしか無い――これがブロッキングの根本原因です。
2PC は、参加者・ネットワークが一時的に落ちても最終的に正しく合意できる(安全性は保つ)プロトコルですが、いつ合意できるかを保証できません。これは分散システムの不可能性(非同期環境で耐障害かつ常に合意できる手段は存在しない)とも整合する性質です。
3PC との違い
ブロッキングを緩和しようと、フェーズを1つ増やしたのが 3相コミット(Three-Phase Commit, 3PC) です。prepare と commit の間に pre-commit を挟みます。
| 観点 | 2PC | 3PC |
|---|---|---|
| フェーズ数 | prepare → commit の2段階 | prepare → pre-commit → commit の3段階 |
| コーディネータ障害時 | 準備済み参加者が無期限にブロックしうる | タイムアウトで参加者側が結末を推定し前進できる |
| pre-commit の意味 | なし | “全員YESを確認済み”を全員に共有してから commit する |
| 弱点 | ブロッキング | ネットワーク分断で誤った前進をしうる(安全性が崩れる) |
| 往復回数・遅延 | 少ない(速い) | 1段階分多い(遅い) |
3PC の狙いは、「全員が YES だった」という事実を pre-commit で全参加者に行き渡らせてからコミットへ進む点にあります。これにより、コーディネータが落ちても、生存参加者は「pre-commit を受け取った=全員 YES だったはず」と推論し、タイムアウト後に自分たちで commit へ前進できます。結果としてブロッキングは減ります。
3PC はノード故障(落ちたまま)を仮定すると前進できますが、ネットワーク分断(落ちていないのに通信が切れる)には弱いのが致命的です。分断された両側が「相手は死んだ」と誤認し、片方が commit、もう片方が abort へ進むと、安全性そのものが破れます。これはCAP 定理が指す、分断時に整合性か可用性かを迫られる構図そのものです。往復が増えて遅い割に保証が不安定なため、現場では採用例が限られます。
実務での落としどころ
完全な原子性を諦めずに可用性も欲しい場合、純粋な 2PC/3PC ではなく次が選ばれがちです。
- Saga パターン: 大きな分散トランザクションを、各サービスのローカルトランザクションの連鎖に分解。失敗したら補償トランザクション(打ち消し処理)で巻き戻す。原子性は緩めるが、ロックを長く握らずブロッキングを避けられる。
- 合意アルゴリズム(Paxos / Raft): コーディネータを単一障害点にせず、過半数の合意でコミット決定そのものを冗長化する。レプリケーションと組み合わせ、ノードが落ちても前進できる(レプリケーションとシャーディング参照)。
「2PC がブロッキングするのは、prepare で YES を返した参加者が in-doubt 状態でコーディネータの決定待ちになり、その決定がコーディネータのログにしか無いから」――この因果を一息で言えるかが分かれ目です。3PC は pre-commit でブロッキングを緩和するがネットワーク分断に弱い、という対比もセットで押さえましょう。
2PC は「複数ノードを原子的にそろえる」という難題への素直な解答で、XA トランザクションや PostgreSQL の PREPARE TRANSACTION など今も広く実装されています。ただしブロッキングという代償は消えません。だからこそ、本当に厳密な原子性が要る所だけ 2PC を使い、それ以外は Saga や合意ベースへ倒す――この使い分けが、分散データ設計の勘所です。
データベース Article
2相コミット(2PC)と分散トランザクションを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
2相コミット
比較で見る軸
難易度: advanced / カテゴリ: データベース / タグ数: 5
導入後に効く点
prepare で YES を返した参加者はコミットもアボートもできず待つしかないため、コーディネータ障害でロックを抱えたまま固まるブロッキング問題がある。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データベース
- タグ数
- 5
判断チェックリスト
- 自社の用途が「2相コミット / 分散トランザクション」に近いか確認する。
- 強みである「2PC はコーディネータが全参加者に prepare で投票を募り、全員 YES なら commit、1人でも NO なら abort して原子性をそろえる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。