Cloud Spannerという「禁じ手」:NoSQLの妥協を過去にするアーキテクチャの真実
「スケールさせるならNoSQL、整合性が必要ならRDB」。
この言葉を、あと何回エンジニアは聞かされるのだろうか。かつては真理だった。しかし、Cloud Spannerの登場によって、その二分法は過去の遺物となった。
今日、私が皆さんに伝えたいのは、「なぜSpannerが単なるRDBではないのか」、そして「NoSQLが抱えていた『整合性の悪夢』から、どうやってSpannerへ脱出すべきか」という設計の核心だ。
—
1. NoSQLが支払ってきた「隠れたコスト」
NoSQL(DynamoDBやCassandraなど)は、シャーディングをアプリケーション側に強いることで、凄まじいスループットを手に入れた。だが、その代償は巨大だ。
- 「あとで整合性が取れる(Eventual Consistency)」という毒: アプリケーション側で競合をハンドリングするロジック(ベクトルクロックやLast-Write-Winsによるデータ消失の許容)が、ビジネスロジックを確実に複雑化させる。
- 「クエリの制限」という足枷: 複雑なJOINや集計ができないため、結果としてデータを非正規化して複数のテーブルに複製する。これが「データの不整合」という時限爆弾を常時抱えることになる。
Spannerは、この「妥協」をアーキテクチャのレベルで破壊した。TrueTime(原子時計とGPSを用いた時刻同期)という物理的裏付けにより、グローバルな外部整合性(External Consistency)を実現しているからだ。
—
2. Spannerは「NoSQLのスケール」をどう手に入れたか
Spannerの真髄は、「自動シャーディング(Split)」にある。
NoSQLでは、キー設計を誤ると「ホットパーティション」が生まれ、性能が破綻する。Spannerは、データ量や負荷に応じて自動的にスプリット(分割)と移動を行う。
設計の教訓:主キー設計の呪縛を解く
Spannerにおいても、主キーの設計は依然として重要だ。だが、NoSQLほど過度に神経質になる必要はない。
— 悪い例: 連続的な数値(シーケンス)を主キーにする
— これでは特定のノードに書き込みが集中し、スプリットの恩恵を受けられない
CREATE TABLE Orders (
OrderId INT64 NOT NULL,
…
) PRIMARY KEY (OrderId);
— 良い例: UUIDやハッシュ化されたプレフィックスを付与する
— 書き込み負荷が全ノードに分散され、Spannerの自動シャーディングが活きる
CREATE TABLE Orders (
OrderId STRING(36) NOT NULL, — UUIDv4などを採用
…
) PRIMARY KEY (OrderId);
—
3. 「複雑なクエリ」を恐れるな
NoSQLでは「JOINは悪」とされ、アプリケーション側でデータをマージしていたはずだ。Spannerは違う。標準SQLによるJOINを、分散環境で効率的に実行できるよう最適化されている。
実務での設計パターン:インターリーブ(Interleave)
Spannerにおける最も強力な武器が「インターリーブ」だ。親子関係にあるテーブルを物理的に同じスプリットに配置し、読み取りのレイテンシを極限まで下げる。
— 親テーブル
CREATE TABLE Users (
UserId INT64 NOT NULL,
…
) PRIMARY KEY (UserId);
— 子テーブルを親にインターリーブさせる
— これにより、Userとそれに関連するOrdersのJOINは、物理的に同じノード内で完結する
CREATE TABLE Orders (
UserId INT64 NOT NULL,
OrderId INT64 NOT NULL,
…
) PRIMARY KEY (UserId, OrderId),
INTERLEAVE IN PARENT Users ON DELETE CASCADE;
この設計は、NoSQLでは「単一テーブル設計」で無理やり実現していたことを、RDBの美しさを保ったまま物理的な最適化として実装できることを意味する。
—
4. パフォーマンス上の注意点:アンチパターンを避ける
Spannerを「ただのMySQL」だと思って運用すると痛い目を見る。
1. Read-Writeトランザクションの肥大化:
Spannerの書き込みは2フェーズコミットを伴う。トランザクション内で重い計算や外部API呼び出しを挟むのは厳禁だ。トランザクションは「短く、速く、小さく」が鉄則。
2. 読み取り専用トランザクションの活用:
分析系のクエリやダッシュボードの表示には、必ず`Snapshot Read`(古い時刻のデータを読み込む読み取り専用トランザクション)を使え。これにより、書き込みロックに影響を与えず、読み取りスループットを無限にスケールできる。
—
結論:エンジニアの設計判断
NoSQLを使う理由は、もう「スケーラビリティ」ではない。単に「コスト」や「特定のデータモデルへの適合」だけで選ぶべきだ。
整合性とスケーラビリティの両立が必要なモダンなシステムにおいて、Spannerを避ける理由はもはや存在しない。設計の複雑さをアプリ側に押し付けるのではなく、「データベースに正しく解かせる」。それこそが、伝説的なアーキテクトが辿り着くべき境地だ。
さあ、次の設計レビューでは、NoSQLの妥協を持ち出すのはやめよう。Spannerという武器を使いこなし、ビジネスの成長に追従する真に堅牢な基盤を構築してほしい。
何か質問があるか?コードレベルのチューニングについてでも、分散トランザクションの深淵についてでも構わない。いつでも聞くがいい。
コメント