【実務・中級編】 データレプリケーションとPaxosグループ – Cloud Spanner

Spannerの核心:Paxosグループとデータレプリケーションの要塞を読み解く

こんにちは、テックリードの私だ。
これまでのデータベース設計の常識を一度捨ててほしい。マスター・スレーブの遅延に泣き、Shardingの境界線引きに深夜までホワイトボードに向かった日々はもう終わりだ。

Cloud Spannerの本質は、グローバル規模で「完全な整合性(External Consistency)」を担保しながらスケールする点にある。だが、その魔法の裏側で何が起きているのかを理解しているエンジニアは驚くほど少ない。「マネージドだからよしなにやってくれる」で設計を放棄していれば、いつか必ずレイテンシの壁やスループットの天井に激突する。

今回は、Spannerの最も深淵な心臓部である「データタブレットとPaxosグループの物理・論理アーキテクチャ」を丸裸にする。コードレビューで「なぜそのスキーマ設計ではレイテンシが跳ねるのか」を論理的に叩き込めるだけの知見を、ここで持ち帰ってほしい。

—

1. タブレットとPaxosグループ:分散の最小単位

Spannerのストレージ層は、リレーショナルテーブルという概念を直接扱っているわけではない。すべてのデータは、階層型キー-バリュー(LSMツリーベースの構造)のフラットな空間にマッピングされ、「タブレット(Tablet)」と呼ばれる単位に垂直分割・水平分割される。

このタブレットこそが、Paxosグループの保護下に入る「実体」だ。

[Spanner Database]
├── Tablet A (Range: 0000 – 5000) ──> Paxos Group A (Leader + Replicas)
├── Tablet B (Range: 5001 – AAAA) ──> Paxos Group B (Leader + Replicas)
└── Tablet C (Range: AAAB – FFFF) ──> Paxos Group C (Leader + Replicas)

1.1 なぜレプリカではなく「Paxosグループ」なのか?

単なる非同期レプリケーションや、お飾り的なRaftによるマスター選出とはワケが違う。Spannerの各タブレットは、独立したマルチプル・レプリカ(通常は3つまたは5つ)のPaxosグループとして動的に構成される。

ここで重要なのは、「書き込みの合意(Quorum)」の単位がタブレットごと、つまりPaxosグループごとであるということだ。
データベース全体で1つの合意を取るのではなく、数千・数万のタブレットが、それぞれ独立したPaxosグループとして並行稼働している。これが、グローバルスケールと高いスループットを両立できる理由の9割を占める。

—

2. リーダー選出とログレプリケーションの深層

では、実際にクライアントからの書き込み(Mutation)が飛んできたとき、Paxosグループ内で何が起きているのか。そのライフサイクルを解剖しよう。

2.1 リーダーの役割と「リース(Lease)」

Paxosグループ内には、常に1つのリーダーレプリカが存在する。だが、ここがポイントだ。通常のPaxosアルゴリズムはスプリットブレインを防ぐための投票にコストがかかる。Spannerはこれを最適化するため、「リーダー・リース(Leader Lease)」を採用している。

  • リーダーは、他のレプリカから「一定期間(例:数秒間)、自分が絶対的なリーダーである」という時間的権利(リース)を買う(合意を取り付ける)。
  • このリース期間内であれば、リーダーは他のレプリカに毎回多数決を取ることなく、単独で読み取り(Read)を処理できる。
  • 書き込み(Write)の際は、Paxosのログレプリケーションフェーズを実行し、過半数(Quorum)の同意を得てコミットする。

2.2 ログレプリケーションのフロー

1. Clientからのリクエスト: クライアントが特定の行を更新するMutationを送信。
2. Leaderへの到達: スパナーのフロントエンド(GFEなど)を経由し、該当タブレットのPaxosリーダーへルーティングされる。
3. Paxos Prepare / Accept: リーダーはログエントリを生成し、自身のローカルログに書き込むと同時に、他の投票権を持つレプリカへRPCで送信する。
4. Quorumの形成: 過半数のレプリカ(例:5つのうち3つ)がログの永続化(Diskへの書き込み)を完了し、ACKを返した時点で、コミットが確定(Committed)する。
5. 適用(Apply)と応答: リーダーはコミットされたログをメモリ上のストレージエンジン(Colossusなど)に適用し、クライアントへ成功を返す。

この一連のフローは、マルチリージョン構成であっても、物理的な光速の限界を除けば驚異的なスピードでチューニングされている。しかし、「光速の限界」は物理法則であり、Googleのエンジニアであっても曲げることはできない。 ここが実務設計の分かれ道になる。

—

3. 現場で生きる設計パターン:なぜそのクエリは遅いのか?

チーフアーキテクトとして最も耐えがたいのは、「Spannerを導入したのにレイテンシが改善しない」という悲鳴だ。その原因のほとんどは、Paxosグループの特性を無視したスキーマ設計にある。

悪い例:ホットスポットとクロスパクロス通信の誘発

例えば、次のような「全行に同じプレフィックスを持つインクリメンタルなID」をプライマリキーにしたテーブル設計を考えてみてほしい。

— 【アンチパターン】全書き込みが単一のタブレット(=単一のPaxosグループ)に集中する
CREATE TABLE Transactions (
TransactionId STRING(64) NOT NULL, — タイムスタンプベースや連番の文字列
UserId STRING(64) NOT NULL,
Amount INT64,
— …
) PRIMARY KEY(TransactionId);

何が起きるか?
1. すべての書き込みキーが辞書順で近接するため、たった1つのタブレットにデータが偏る。
2. そのタブレットを管理する1つのPaxosグループに、全トラフィックが殺到する(ホットスポット)。
3. リーダーのCPUが枯渇し、いくらオートスケーリングを期待しても、1つのタブレットのリーダーは1台のマシン上でしか動かないため、スケーリングの恩恵を受けられない。

良い例:インターリーブと分散キーの極意

では、どう設計すべきか。

— 【推奨パターン】テナントIDやユーザーIDのハッシュをプレフィックスにし、インターリーブを活用
CREATE TABLE Users (
UserId STRING(64) NOT NULL,
CreatedAt TIMESTAMP,
Data BYTE(MAX),
) PRIMARY KEY(UserId);

— 子テーブルを親と同一のPaxosグループに強制配置
CREATE TABLE UserTransactions (
UserId STRING(64) NOT NULL,
TransactionId STRING(64) NOT NULL,
Amount INT64,
) PRIMARY KEY(UserId, TransactionId),
INTERLEAVE IN PARENT Users ON DELETE CASCADE;

アーキテクチャ上のメリット:

  • `UserId` のハッシュや十分に分散されたキーを使うことで、データとトラフィックが数千・数万のPaxosグループへ綺麗に水平分散される。
  • `INTERLEAVE IN PARENT` を使うことで、`Users` とその子である `UserTransactions` が物理的に同一のタブレット(=同一のPaxosグループ)にコロケーション(共存)される。
  • これにより、ユーザーに関連するトランザクションの結合や取得が、ネットワークをまたぐクロスパクロス通信なしで、ローカルなメモリ・ストレージ参照だけで完結する。

—

4. パフォーマンス上の注意点とオペレーショナル・マインド

最後に、実運用において絶対に押さえておくべき「Paxosとレプリケーションの急所」を挙げておく。

1. マルチリージョン構成での「TrueTime」の代償
Spannerの強みである外部整合性は、Googleが誇る原子時計とGPSによる「TrueTime API(不確実性の幅 $\epsilon$ を持つ時刻)」によって支えられている。マルチリージョン(例:`nam-eur-asia`)で書き込みを行う場合、コミット待ち時間は「光速の物理的距離によるRTT(往復遅延)」に支配される。レイテンシ要件がミリ秒単位でシビアなシステムでは、マルチリージョンのPaxosグループ配置(リーダーの置き場所)をビジネス要件と照らし合わせて厳密に設計しなければならない。
2. トランザクションの競合とPaxosの再試行(Abort)
楽観的同時実行制御(OCC)を採用しているSpannerでは、複数のトランザクションが同一のPaxosグループ内の行に対して同時に書き込みを行うと、競合が発生してアボート(ロールバック)し、クライアント側でリトライが必要になる。高頻度で更新される単一のカウンターやメタデータ行を安易に作らないこと。これは分散データベース設計の鉄則だ。

—

結びに代えて

Cloud Spannerは、データベースの面倒なスケーリングや可用性の担保を抽象化してくれる。しかし、その内部でうねる「タブレットとPaxosグループのダイナミクス」を理解していなければ、宝の持ち腐れどころか、予期せぬパフォーマンス劣化に足をすくわれることになる。

コードレビューの場で「このスキーマだとどのタブレットに負荷が偏る? Paxosのクォーラムはどう構成される想定?」とクールに問いかけられるエンジニアであれ。
アーキテクチャの底面を理解した者だけが、Spannerの真のポテンシャルを解放できる。さあ、設計図を書き直そう。

コメント

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