【実務・中級編】 NewSQLアーキテクチャ – Cloud Spanner

Cloud Spannerという「神の技術」:分散トランザクションの限界を突破する設計思想

「RDBMSの整合性」と「NoSQLのスケールアウト」。この二つは、長らくトレードオフの代名詞だった。CAP定理の呪縛に縛られ、我々は「読み取り専用のレプリカ」や「シャーディングによる複雑なアプリケーション改修」という名の苦行を強いられてきた。

だが、Cloud Spannerの登場でその歴史は終わった。これは単なるマネージドデータベースではない。「TrueTime」という物理的な制約を技術でねじ伏せた、現代分散システムの到達点だ。

本稿では、このアーキテクチャの本質を理解し、実戦で「Spannerを殺さない」ための設計指針を授ける。

—

1. TrueTime:物理時間を「信頼」に変えるアーキテクチャ

Spannerの心臓部は、各ノードに搭載された原子時計とGPS受信機による「TrueTime API」だ。これが何を意味するか。それは、「世界中のノードが、ほぼ誤差ゼロで現在時刻を共有できる」ということだ。

従来の分散DBが「論理クロック」や「ベクタークロック」という複雑な整合性確保に命を削っていたのに対し、Spannerは物理時間を用いてシリアライズ可能な(Serializable)トランザクションをグローバル規模で実現した。

エンジニアへの教訓:
「Spannerは遅い」と嘆くエンジニアの多くは、このTrueTimeの恩恵を逆手に取った設計をしていない。物理的に離れたリージョン間でのコミット待機時間(Commit Wait)を理解せず、無闇にトランザクションを細分化してはいけない。

—

2. スキーマ設計の「黄金律」:SplitsとInterleaving

Spannerのストレージは、キーの範囲(Range)ごとに「Split」として動的に分割される。システムがスケールするということは、このSplitが自動的に分割・移動し続けるということだ。

避けるべき設計:ホットスポット

連番(`UUID`の先頭など)を主キーにすると、書き込みが常に一つのSplitに集中し、Spannerの水平スケール能力を殺すことになる。

推奨パターン:

  • 主キーをビット反転させるか、ハッシュ化して分散させる。
  • 書き込み性能が必要な場合は、キーの先頭にシャーディング用のプレフィックスを付与する。

活かすべき設計:テーブルのインターリーブ(Interleaving)

親テーブルのレコードと子テーブルのレコードを物理的に同じSplitに配置する手法だ。

— ユーザーと注文を関連付ける例
CREATE TABLE Users (
UserId INT64 NOT NULL,
— …
) PRIMARY KEY (UserId);

CREATE TABLE Orders (
UserId INT64 NOT NULL,
OrderId INT64 NOT NULL,
— …
) PRIMARY KEY (UserId, OrderId),
INTERLEAVE IN PARENT Users ON DELETE CASCADE; — 親と物理的に同じ場所に格納

これにより、`JOIN`や親のキーによるサブクエリがネットワーク越しの通信を発生させず、ローカルメモリ上での完結を可能にする。これはパフォーマンスの桁を一つ変える。

—

3. 実務で「刺す」ためのトランザクション設計

Spannerで最も高価なのは「トランザクション」だ。しかし、適切に使えば最強の武器になる。

読み取りと書き込みの分離

読み取り専用トランザクション(Snapshot Read)を活用せよ。

// スナップショット読み取りの例
// タイムスタンプを指定することで、過去の整合性のある状態を読める
txn, _ := client.ReadOnlyTransaction().WithTimestampBound(s.ExactStaleness(15 time.Second))
defer txn.Close()

// これにより、読み取り負荷を書き込みの競合から完全に分離できる

実務上の鉄則:
1. 読み取りは可能な限りReadOnlyTransactionで行う: ロック不要で、CPU負荷も低い。
2. トランザクション内にはロジックを詰め込まない: トランザクションは「短く、速く」が鉄則だ。外部API呼び出しなどをトランザクション内に含めるのは、システムの死を意味する。

—

4. パフォーマンスチューニング:なぜ「そのクエリ」は遅いのか?

Spannerでクエリが遅い場合、原因は9割が「フルスキャン」か「ロック待ち」だ。

  • インデックスの戦略: 必要なカラムだけをインデックスに含め(`STORING`句)、検索対象を絞れ。
  • パラメータ化クエリ: スキーマの解析を効率化し、プランキャッシュを有効にするため、リテラル値をクエリに直書きしてはいけない。

— NG: リテラルをハードコード
— SELECT FROM Orders WHERE OrderId = 12345

— OK: パラメータ化クエリ
— SELECT FROM Orders WHERE OrderId = @orderId

—

最後に:Spannerは「銀の弾丸」ではない

Cloud Spannerは、従来型RDBMSの設計思想を捨て、分散システムの現実を受け入れた上で勝利を掴むためのツールだ。

「とりあえずRDB感覚で設計して、あとでスケールさせればいい」という甘い考えは通用しない。データアクセスパターンを物理レベルで想像し、Splitの境界を意識し、TrueTimeの恩恵を最大限に引き出す。

君たちがコードレビューで「このトランザクションの範囲は適切か?」「主キーの分散は考慮されているか?」を問い続けることで、初めてSpannerはその真価を発揮する。

さあ、恐れることはない。最高の道具を手にしたのだから、あとは君たちが最高のアーキテクチャを築き上げるだけだ。

コメント

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