1. 全員一致で確定する — 2相コミット(2PC)
複数ノードにまたがる更新を「全部か無か」で確定するための古典的な仕組みが2相コミットです。コーディネータ(進行役)が2段階でみんなに聞きます。フェーズ1(準備): 「コミットできる?」と全員に問い、各参加者が Yes/No を投票。フェーズ2(確定): 全員がYesのときだけコミット、一人でもNoなら全員ロールバック。下で各参加者の投票を切り替えて、1人でもNoだと全体が取り消される様子を見てください。
2相コミット — 準備の投票 → 全員一致でコミット
上=コーディネータ、下=参加者3ノード。フェーズ1で「準備して」を送り、各ノードが投票(緑=Yes・準備OKでロック保持🔒/赤=No)。フェーズ2で判定を配信。ボタンでどれかをNoにすると、他がYesでも全員ロールバックになります。
POINT — なぜ「準備」を挟むのか
いきなりコミットさせると、あるノードがコミット済みなのに別ノードが失敗、というちぐはぐな状態が起きうる。準備フェーズで全員に「コミットできると約束(ログに記録しロック保持)」させておけば、フェーズ2の指示は必ず実行できる。だから「約束 → 実行」の2段階に分ける。
2. 進行役が倒れると固まる — ブロッキング問題
2PCには致命的な弱点があります。全員が「準備OK」と投票した直後にコーディネータが落ちると、参加者たちは宙づりになる。自分はコミットすると約束してロックを握ったまま、でも最終指示(コミット?中止?)が来ない。単独ではどちらも選べない ― 勝手にコミットすれば他がNoだったかもしれず、勝手に中止すれば他はコミットしたかもしれない。だから進行役の復帰をひたすら待って固まる(ブロック)。下のボタンで進行役を落として、参加者がロックを握ったまま止まる様子を見てください。
ブロッキング問題 — 進行役が落ちると参加者が宙づりに
通常時は 準備 → コミット がすんなり流れます。落とすと、参加者は「準備OK・ロック保持🔒」の状態で判断不能(in-doubt)のまま停止。ロックを握り続けるので、他のトランザクションも巻き込んで待たされます。これを解くのが次の合意アルゴリズムです。
注意 — 2PCは「安全だが止まりうる」
2PCは整合性(誰もちぐはぐにならない)は守るが、可用性は守らない。単一のコーディネータが単一障害点になり、落ちれば全員ブロック。タイムアウトで諦めても、in-doubt な参加者は勝手に決められない。この「1人の進行役に運命を握らせる」構造が根本問題だ。
3. 同じデータを複数持つ — レプリケーション
1つのノードが落ちても平気なように、同じデータを複数ノードに複製(レプリケーション)します。よくある形がリーダー/フォロワー: 書き込みは必ずリーダーが受け、フォロワーへコピーを流す。ここで選択が生じます。同期=フォロワーの書き込み完了を待ってからクライアントに「確定」を返す(安全・でも遅い)。非同期=待たずに即返す(速い・でも落ちると直近が消える)。下でモードを切り替え、リーダーを落としてフェイルオーバー(昇格)とデータロスを見てください。
レプリケーション — 同期/非同期とフェイルオーバー
数字=各ノードが持つ書き込み件数(log)。同期はフォロワーが追いつくまで待つので常に横並び → 落としてもロス0。非同期はリーダーが先行しフォロワーが遅れる → 落とすと、まだ複製されていなかった書き込みがロストします。落とすと最新のフォロワーが新リーダーに昇格します。
同期: 安全(ロス0)・遅い ⟷ 非同期: 速い・フェイルオーバーで直近をロス
多くの実システムは折衷(準同期: 少なくとも1台のフォロワーのackを待つ)で、安全性と速度のバランスを取る
4. 過半数で決める — 合意アルゴリズムと最前線
2PCの「1人の進行役に依存して固まる」弱点を克服したのが合意アルゴリズム(Raft・Paxos)。鍵は過半数(多数決)です。リーダーが落ちても、残りのノードが投票して過半数の支持を得た1台を新リーダーに選出し、処理を継続する。過半数さえ生きていれば止まらない。下でリーダーを落として、フォロワーが立候補し票を集めて新リーダーになるリーダー選出を見てください。
Raftのリーダー選出 — 過半数の票で新リーダーを決める
5ノード。通常はリーダー(王冠)が♥ハートビートを送ります。落とすと、あるフォロワーがタイムアウトして立候補(term++)し、他ノードに投票を依頼。過半数(3票以上)を集めた瞬間に新リーダー確定。過半数が生きていれば選出は必ず成功します。
POINT — なぜ「過半数」なのか
過半数どうしは必ず重なる(2つの過半数集合には共通ノードがある)。だから「古いリーダー派」と「新リーダー派」が同時に成立することはなく、分裂(スプリットブレイン)を防げる。奇数台(3・5・7)にするのは、過半数を明確にし、1〜2台の故障に耐えるため。
一歩先へ — NewSQL、そしてスケールとACIDの和解
かつては「スケールしたければ ACID を諦めよ(NoSQL)」が定説だった。だが Google Spanner が、Raft/Paxos で複製した各シャードを高精度時計(TrueTime)で束ね、地球規模でも強整合トランザクションを実現。以後 CockroachDB・TiDB・YugabyteDB といったNewSQL / 分散SQLが、SQLとACIDを保ったまま水平スケールする道を開いた。分散トランザクションの最前線は「スケールか整合性か」ではなく「両方を、いくらのコストで」を競う段階に来ている。
5. まとめ — 分散で「確定」を守る道具箱
- 2相コミット:準備の投票 → 全員一致でコミット。整合性は守るが、進行役が落ちるとブロックする。
- ブロッキング問題:単一のコーディネータが単一障害点。in-doubt な参加者はロックを握って固まる。
- レプリケーション:複製で耐障害性を得る。同期は安全で遅い、非同期は速いがフェイルオーバーでロス。
- 合意アルゴリズム:過半数の投票でリーダーを選び直し、少数の故障でも止まらない(Raft/Paxos)。
一歩先へ — 不可能性の壁と、それでも動くシステム
理論的には、非同期ネットワークで1台でも落ちうる状況では合意を必ず有限時間で達成する決定的アルゴリズムは存在しない(FLP不可能性)。RaftやPaxosは、タイムアウトという「現実的な仮定」を1つ足すことでこの壁を実務的に回避している。完璧な保証はなくても、ほぼ確実に・十分速く動くよう設計する ― それが分散システム工学の勘所だ。