【Cloud Spanner 伝説のアーキテクチャ講座】ジオパーティショニング:データムラを制する者がグローバルを制す
こんにちは。テックリードの私だ。
今日の設計レビュー、なかなか面白い課題が持ち上がったな。「グローバル展開するSaaSにおいて、EU圏のユーザーデータをGDPR等の規制遵守のためにEU域内に完全に物理固定しつつ、グローバル共通のマスターデータとは低レイテンシで結合したい」という要件だ。
君たちはこれをどう設計する? 「素直にリージョンごとにDBを分ける」「アプリケーション層でシャワー状にルーティングする」――そんな前近代的な答えを出したとしたら、今日のレビューは不合格だ。
Cloud Spannerには、この難問を美しく、かつトランザクションの整合性を保ったまま解決する唯一無二の武器がある。それが 「ジオパーティショニング(Geo-partitioning)」 だ。
今回は、単なるドキュメントの斜め読みでは絶対に辿り着けない、実務の現場で生き抜くための極限の知見を授けよう。
—
1. ジオパーティショニングの本質:なぜ「ただのマルチリージョン」ではダメなのか?
Cloud Spannerの標準的なマルチリージョンインスタンス(例: `nam-eur-asia1`)は、世界中のユーザーに均一な高可用性と低レイテンシを提供するため、データを自動的に分散・レプリケーションする。CAP定理の迷宮をGoogleの分散合意アルゴリズム(Paxos)で華麗に突破した傑作だ。
しかし、ここで 「データ主権(Data Sovereignty)」 という冷徹なビジネス要件が立ちはだかる。
「ドイツの顧客データを、アメリカのディスクに1バイトたりとも保存してはならない」。この要件に対し、標準のマルチリージョン構成では、データの物理的な配置先をピンポイントで制御できない(どこかのレプリカが海を渡る可能性がある)。
ここで登場するのがジオパーティショニングだ。
これは、「特定の行(Row)データを、指定した単一のリージョン(例: `europe-west3`)のストレージノードに物理的に釘付け(Pin)にする」 機能である。
[グローバル クライアント層]
│
├──────────────────────┐
▼ ▼
[US East ユーザー] [EU West ユーザー]
│ │
▼ ▼
USリージョンのノード EUリージョンのノード (ジオパーティション)
│ │
└──────────┬───────────┘
▼
[Spanner データベース]
単にリージョンを分けるのとは違う。「単一のデータベース、単一のグローバルなスキーマ、しかしデータムラ(物理配置)は完全に制御されている」 という、分布式データベースの聖杯のような状態を作り出せるのだ。
—
2. 堅牢な設計パターン:プレースメントキーによる行レベルの物理制御
ジオパーティショニングを実装する上で、Spannerの内部構造を理解しているかどうかが分かれ目になる。
Spannerでは、インタリーブ(Interleave)テーブル構造と プレースメントキー(Placement Key) を組み合わせて使用する。
以下の実務レベルのスキーマ定義を見てほしい。
— 1. テナント(顧客)ごとのメタデータを管理する親テーブル
CREATE TABLE Tenants (
TenantId INT64,
CountryCode STRING(2),
Name STRING(1024),
) PRIMARY KEY (TenantId);
— 2. 実際の業務データ(例:監査ログ)を格納する子テーブル
— 親のTenantIdに紐づき、かつプレースメントキーによって物理配置を制御する
CREATE TABLE AuditLogs (
TenantId INT64,
LogId INT64,
Timestamp TIMESTAMP,
Payload STRING(MAX),
) PRIMARY KEY (TenantId, LogId),
INTERLEAVE IN PARENT Tenants ON DELETE CASCADE;
ここに、ジオパーティショニングの命である バウンダリー(境界)テーブル とプレースメント定義を適用する。
— 3. データの物理配置(プレースメント)を定義するポリティステーブル
— このテーブルの行キーの範囲ごとに、どのリージョンにデータを置くかを指定する
CREATE TABLE TenantPlacements (
CountryCode STRING(2),
TenantId INT64,
Region STRING(MAX),
) PRIMARY KEY (CountryCode, TenantId);
— 4. 実際のジオパーティションの紐付け(データベースレベルのポリスティ設定)
— ※実際にはALTER DATABASE構文やスプリットポイントの制御、プレースメントポリシーを使用する
ALTER TABLE AuditLogs SET LOCATION (
— プレースメントキーに基づいたリージョン割り当ての概念
);
💡 チーフアーキテクトの視点:設計上の鉄則
1. パーティションの粒度を間違えるな
ジオパーティショニングの最小単位は「スプリット(Split)」、すなわち行のグループだ。個々の行単位ではなく、テナントIDや国コードといったプレースメントキーのプレフィックス単位で設計しなければ、メタデータのオーバーヘッドでパフォーマンスが破綻する。
2. ホットスポットの回避
特定のEU大企業テナントにトラフィックが集中した場合、そのテナントが配置された単一のリージョンノード(およびリーダーレプリカ)に負荷が集中する。マルチリージョンの負荷分散のメリットが一部スポイルされる点を忘れるな。
—
3. 実装と運用におけるパフォーマンス上の罠
コードレビューでよくあるバグが、「ジオパーティションされたデータに対して、グローバルをまたぐ巨大なJOINクエリを無計画に発行する」ことだ。
❌ 悪い例:レイテンシの爆弾を生むクエリ
— EUのデータとUSのデータを無造作に結合する
SELECT
t.Name,
l.Payload
FROM Tenants t
JOIN AuditLogs l ON t.TenantId = l.TenantId
WHERE t.CountryCode = ‘US’ AND l.Timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY);
もし`Tenants`(US配置)と`AuditLogs`(EU配置)の結合がクロスリージョンで発生した場合、Spannerのクエリエンジンはネットワークの海を越えてデータをシャッフルし、レイテンシが数十ミリ秒〜数百ミリ秒に跳ね上がう。
⭕ 正しい例:ローカリティを意識したクエリ設計
ジオパーティショニングの恩恵を最大限に受けるには、「クエリの実行もデータの配置場所で完結させる(Colocation)」 ことだ。
— アプリケーション側でルーティングされたリージョンエンドポイント、
— またはプレースメントキーを確実にWHERE句の最初に置く
SELECT
TenantId,
LogId,
Payload
FROM AuditLogs
WHERE TenantId = @TargetTenantId — パーティションキーを明示
AND Timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY);
Spannerのオプティマイザは、`TenantId` が特定のジオパーティションに固定されていることを検知すると、クエリの実行計画をそのリージョンのストレージノードへプッシュダウン(Pushdown) する。これにより、ネットワークホップを最小限に抑えた超高速なローカル読み出しが可能になる。
—
4. 本番運用のチェックリスト:ここを見落とすな
最後に、実務でこのアーキテクチャを本番投入する前に、私が必ず確認するチェックリストを置いておく。
1. データ移行(Backfill)のコスト計算
既存のグローバルテーブルをジオパーティション化する場合、大規模なデータの物理的な再配置(データマイグレーション)がバックグラウンドで走る。I/O使用率の急増に備えよ。
2. 法的要件(コンプライアンス)の監査証跡
「本当にそのデータが指定リージョン外に出ていないか?」を証明するため、Cloud LoggingとCloud Monitoringを組み合わせて、ストレージノードの物理リージョンアクセスをモニタリングするダッシュボードを作っておくこと。
3. フェイルオーバー時の挙動確認
万が一、指定したEUのプライマリリージョンが大規模障害でダウンした場合の挙動(RPO/RTO)をカオスエンジニアリングでテスト済みか? ジオパーティションであっても、Spannerの自動フェイルオーバメカニズムがどう働くかを把握しておけ。
—
結びにかえて
ジオパーティショニングは、「グローバルデータベースの利便性(強整合性・単一スキーマ)」 と 「ローカル要件の厳格さ(データ主権・超低レイテンシ)」 という、一見して矛盾する二つの欲求を高い次元で調停する究極のアーキテクチャだ。
これを使いこなせれば、君たちのシステムは単なる「クラウド上のDBを使ったシステム」から、真にグローバルスケールで戦える「レジリエントなプラットフォーム」へと進化する。
今日のレビューはここまでだ。
さあ、コードを書き直して出直してくれたまえ。期待している。
コメント