「ノード数」という幻想を捨てろ:Cloud SpannerにおけるProcessing Units(PU)の本質と最適設計
エンジニア諸君。Spannerの設計において、いまだに「ノード数」という粒度でスケーリングを語っていないだろうか?
かつて、Spannerの計算リソースは「1ノード」という巨大な単位でしか制御できなかった。しかし、今の我々にはProcessing Units (PU)という極めて精密なメスがある。1ノード=1000 PUという定義を単なる換算式として覚えるのは、アーキテクトとしては三流だ。
今回は、このPUをいかに使いこなし、コストとパフォーマンスの極限をトレードオフするか。その核心を紐解いていく。
—
1. 「なぜPUなのか」――粒度の革命
従来の「ノード」単位の増減は、大規模なバッチ処理や突発的なアクセス増に対してはあまりに大雑把だった。1ノード追加するだけで数千円/時のコスト増だ。
PUは100 PU(0.1ノード相当)単位での調整を可能にした。これは、「負荷の予兆に対して、必要な分だけリソースを差し込む」という、極めてモダンなスケーリングを可能にする。
- 100 PU: 小規模な開発環境、あるいは低頻度のメタデータ管理。
- 1000 PU: 従来の1ノード相当。一般的な高負荷プロダクションのベースライン。
- X00 PU: ここが重要だ。我々が設計すべきは、常に1000の倍数ではない。ワークロードの特性を見極め、700 PUや1200 PUといった「真に必要な値」を定義する。
2. 実務で直面する「PUの罠」と設計パターン
PUを調整する際、多くのエンジニアが陥る罠がある。それは「CPU使用率だけを見てPUを上げ下げする」ことだ。
アンチパターン:リアクティブなPU調整
負荷が上がったからPUを増やす。これはSpannerの「分散性能」を殺す。Spannerがリソースを再配分し、スプリット(データ分割)を再調整して安定稼働するまでには、物理的なタイムラグがある。
推奨パターン:ワークロード分離による定常設定
Spannerの真価は、「アクセスパターンに応じてPUを割り振る」ことにある。
1. 予測可能な負荷: 定時バッチなどは、実行の30分前にAuto-scalerやAPIでPUを増強し、完了後に戻す。
2. 不規則な負荷: 読み取り専用のトラフィックが多いなら、PUを増やす前にReadOnlyレプリカの構成を見直す。PUは「書き込み性能(強整合性)」のキャップであることを忘れてはならない。
—
3. パフォーマンスチューニング:PUを「無駄にしない」ために
PUをどれだけ増やしても、アプリケーション側の書き込みパターンが悪ければ、単に「高価な待ち行列」を作るだけだ。
以下のコード(Goクライアント)を見てほしい。これがPUを効率的に使い切るための設計だ。
// 悪い例: シーケンシャルなID発行によるホットスポット
// これではいくらPUを増やしても、特定の分割(スプリット)に負荷が集中し、
// PUの計算リソースが死蔵される。
mutation := spanner.Insert(“Users”, []string{“UserId”, “Data”}, []interface{}{uuid.New().String(), “…”})
// 良い例: ランダムなキー設計とバッチ処理
// PUを最大限活用するには、負荷をクラスター全体に均一に散らす必要がある。
// Writeは可能な限りMutationGroupでまとめて送信する。
m := []spanner.Mutation{
spanner.Insert(“Orders”, columns, values1),
spanner.Insert(“Orders”, columns, values2),
}
_, err := client.Apply(ctx, m) // バッチ送信でオーバーヘッドを削減
PU最適化のためのチェックリスト
- ホットスポットの排除: 主キーの先頭カラムが単調増加していないか?(UUID v4やビット反転の使用を検討せよ)
- スプリットの状況確認: `SPANNER_SYS.TABLE_SIZES` を見ろ。特定のPUが常に100%に張り付いているなら、それはPU不足ではなく「データ設計の敗北」だ。
- トランザクションの粒度: 巨大なRead-WriteトランザクションはPUを占有する。細分化せよ。
—
4. チーフアーキテクトからの提言
PUは単なる「設定値」ではない。君たちが設計する「システムの応答速度とコストの境界線」そのものだ。
- 開発環境: 100 PUで十分だ。これでリソースを浮かせ、その分を本番環境の冗長性や、より高度なインデックス設計のための検証に回せ。
- 本番環境: 常に「CPU使用率65%」をターゲットにせよ。それ以上は突発的なスパイクで遅延を生むリスクがある。それ以下はコストの無駄だ。
Cloud Spannerは、現代のRDBの頂点だ。この強大な力を扱うには、ノード数という過去の物差しを捨て、PUという精密なメスでシステムを彫刻する気概が必要だ。
設計において「なぜそのPU値にしたのか?」と問われて、明確なメトリクスとスループットの根拠を答えられないのであれば、それは設計ではない。ただの「お祈り」だ。
次にコードを書くとき、そして次にスケーリングの設計をするとき、思い出してほしい。「PUは、君たちが書いたクエリの効率を映し出す鏡である」ということを。
コメント