【実務・中級編】 Paxos合意アルゴリズム – Cloud Spanner

Cloud Spannerの心臓部:Paxosと分散合意のリアルワールド

こんにちは。テクニカルリードの私だ。
今日のコードレビュー、あるいは先日の障害ポストモーテムで、君たちはこんな言葉を口にしていないか?

  • 「Spannerはマネージドだから、トランザクションの裏側の整合性は勝手に担保される」
  • 「リプリカを増やせば、読み取りも書き込みも無条件にスケールする」
  • 「スプリットが移動しても、レイテンシには大して影響しないだろう」

――甘い。甘すぎる。
Google Cloud Spannerが「究極のデータベース」と称されるのは、魔法が使われているからではない。物理法則の制約、そして分散システムにおける合意形成の極限的な最適化が、コードの隅々にまで行き届いているからだ。

今回は、Spannerのコアアーキテクチャの心臓部である Paxos合意アルゴリズム に真正面からメスを入れる。教科書的な「リーダー選出と過半数の合意」という説明で分かった気になるのはもう終わりだ。実務の現場でインフラストラクチャをどうハックし、どのような設計判断を下すべきか。その「生きた知見」を授けよう。

—

1. なぜSpannerにPaxosが必要なのか?(根本思想の理解)

まず前提を共有しよう。Spannerは「グローバルにスケールし、外部整合性(External Consistency)を保証するリレーショナルデータベース」だ。これを単一の障害点(SPOF)なしで実現するため、データをシャーディング(Spanner用語では スプリット / Split)し、各スプリットを複数リージョンにまたがる複数のノード(レプリカ)で冗長化している。

ここで発生する永遠の問い:
> 「ネットワークが分断されようが、一部のノードが吹き飛ぼうが、世界中のクライアントが書き込んだデータの順序と内容を、どうやって完全に一致させるのか?」

ここで登場するのが Paxosアルゴリズム だ。

トランザクション処理におけるPaxosの役割

Spannerの書き込み(Write)は、単なるストレージへのI/Oではない。
1. クライアントがリーダーレプリカに書き込みを要求する。
2. リーダーは Paxosグループ(通常は5つまたは3つのレプリカノード)の過半数(Quorum)に対して、その変更(ログエントリ)を提案(Propose)する。
3. 過半数が「同意(Accept)」した時点で、その書き込みは コミット可能 と判定される。

> チーフアーキテクトの視点:
> よくある誤解だが、Spannerの全データが一つの巨大なPaxosグループで動いているわけではない。データは細切れのスプリットに分割され、スプリットごとに独立したPaxosグループが並行して稼働している。つまり、君のテーブルの行Aと行Bは、物理的に全く異なるPaxosグループによって合意が形成されているのだ。ここを理解していないと、ホットスポットを踏んだときに「なぜか一部のパーティションだけがスロットリングされる」現象の理由が一生分からない。

—

2. 現場の設計者が知るべき「Paxosとレイテンシ」の残酷な真実

実務でSpannerを扱う際、開発者が最も直面する壁は レイテンシ だ。特にマルチリージョン構成(例: `nam-eur-asia` や `asia-northeast1` を含むカスタムマルチリージョン)では、物理的な光速の壁が牙を剥く。

Quorum(過半数)の物理的制約

Paxosの合意には過半数の承認が必要だ。

  • 3レプリカ構成の場合:2ノードの合意が必要。
  • 5レプリカ構成の場合:3ノードの合意が必要。

もし、東京(`asia-northeast1`)、大阪(`asia-northeast2`)、アイオワ(`us-central1`)の3拠点でPaxosグループを組んだとしよう。東京のリーダーが書き込みを受け、アイオワのレプリカに提案を投げ、その返答を待つ必要がある。
東京・アイオワ間の往復レイテンシ(RTT)はおよそ130ms〜150msだ。つまり、物理的にどれだけコードを最適化しても、同期書き込み(Commit)のベースラインレイテンシは100msを下回ることができない。

💡 堅牢な設計パターン:マルチリージョン配置の最適化

「グローバルだからどこに置いても一緒」という幻想を捨てろ。書き込みレイテンシがビジネス要件(例: 決済APIで50ms以内)にシビアな場合、Paxosグループの配置は以下のように設計せよ。

[リージョンA (リーダー)] —- (高速ネットワーク) —- [リージョンB (同期レプリカ)]
\ /
\———– (遠隔地・非同期に近い扱い) ——-/ [リージョンC (証人/Witness)]

  • Witness(証人)ノードの活用:

Spannerのレプリカには、データを保持せずPaxosの投票権(Vote)だけを持つWitnessを配置できる。ストレージコストとクロスリージョン転送コストを抑えつつ、過半数のQuorumを維持し、可用性を担保する極めて実用的なパターンだ。

—

3. リーダーシップの移転と「スプリット・ブレイン」の回避

Paxosが強靭な理由は、スプリット・ブレイン(脳味噌分割障害)を防ぎながら、リーダー障害時に自動で新しいリーダーを選出できる点にある。

リーダー選出の裏側

SpannerのPaxosグループでは、リーダーが定期的にハートビート(心拍)をフォロワーに送り続けている。
もしネットワーク一時断などでフォロワーがリーダーからのハートビートを一定期間(数秒)検知しなくなると、リーダー選挙(Leader Election)がトリガーされる。

ここでPaxosのイミュータビリティ(不変性)が活きる。
新しいリーダーが選出される際、過去のすべてのコミット済みログが引き継がれていることが数学的に保証されるため、古いリーダーが「自分がまだリーダーだ」と勘違いして書き込みを受け付けたとしても、過半数の合意を得られないためシステム全体の整合性は守られる。

> コードレビューでの指摘ポイント:
> アプリケーション側で「DBのコネクションが突然切れたから、すぐにリトライしよう」と実装するケースがある。
> Spannerのドライバは、リーダーのフェイルオーバーやPaxosの再選挙を裏側で極めて高い精度でハンドリングする。しかし、短時間に大量のトランザクションが集中した瞬間にリーダーの負荷が跳ね上がり、一時的なレイテンシスパイク(Tail Latency)が発生することは覚えておけ。リトライアルゴリズムには必ず「指数バックオフ(Exponential Backoff)とジッター(Jitter)」を組み込め。

—

4. パフォーマンス上の注意点:Paxosスロットリングとホットスポット

実務で最も恐ろしいのは、Paxos層での「スロットリング(Throttling)」だ。
Cloud Spannerのモニタリング(Cloud Monitoring)で `High Priority CPU` や `Transaction Latency` が跳ね上がったとき、君は何を疑うべきか?

1. シーケンシャルなキー設計の罪

よくあるアンチパターンとして、オートインクリメント的なIDや、現在時刻(`timestamp`)をプライマリキーの先頭に置く設計がある。

— 【アンチパターン】これでは特定のPaxosグループに書き込みが集中する
CREATE TABLE EventLogs (
EventTime TIMESTAMP NOT NULL, — 先頭に時系列データを置くのは悪夢
EventId STRING(64) NOT NULL,
Payload STRING(MAX),
) PRIMARY KEY (EventTime, EventId);

なぜこれがPaxosを殺すのか?
Spannerのスプリットはキー範囲で分割される。`EventTime` が現在時刻であるかぎり、世界中のすべての書き込みが「現在時刻のレンジを担当する単一のスプリット(=単一のPaxosグループ)」に集中する。
その結果、そのPaxosグループのリーダーノードのCPUが飽和し、Paxosの合意形成プロセスが遅延し、システム全体のスループットが完全に死ぬ。

🛠️ 改善策:ハッシュ化による負荷分散(Bit-Reversal)

キーの先頭にハッシュ値を付与するか、UUIDv4(完全にランダムなもの)を使い、書き込みを複数のPaxosグループに美しく分散させよ。

— 【推奨パターン】ハッシュプレフィックスでPaxosグループを分散
CREATE TABLE EventLogs (
ShardId INT64 NOT NULL, — 0〜15程度のハッシュ値
EventTime TIMESTAMP NOT NULL,
EventId STRING(64) NOT NULL,
Payload STRING(MAX),
) PRIMARY KEY (ShardId, EventTime, EventId);

これにより、書き込みは16個の異なるPaxosグループに並行して分散され、Paxosの合意形成がボトルネックになるのを防ぐことができる。

—

5. チーフアーキテクトからの最終提言

Cloud SpannerのPaxos合意アルゴリズムは、単なる教科書のアルゴリズムの実装ではない。それは、「物理的な距離と不確実なネットワークの上で、あたかも単一の完璧なマシンが存在するかのように振る舞わせる」ための執念のエンジニアリングだ。

君たちが設計・実装を行う際、常に頭に入れておくべき原則は以下の3つだ。

1. すべての書き込みの裏には「Paxosの過半数合意」という物理的コストがあることを忘れるな。
2. データのスパース性(散らばり)を意識せよ。ホットスポットはPaxosのリーダーを殺す。
3. マルチリージョンのレイテンシは、コードでは消せない。アーキテクチャで調停せよ。

仕組みの本質を理解したエンジニアだけが、真にスケーラブルで堅牢なシステムを構築できる。
次の設計レビューでは、単に「動くコード」ではなく、「Paxosの負荷まで計算し尽くされたアーキテクチャ」を持ってくることを期待している。

さあ、コードを書こう。

コメント

タイトルとURLをコピーしました