【実務・中級編】 テーブル定義とデータ型 – Cloud Spanner

Cloud Spannerの真髄:テーブル設計は「分散環境の物理」を理解することから始まる

Spannerをただの「SQLが使えるNoSQL」だと思っているなら、今すぐその認識を捨ててほしい。Spannerは、Googleが数十年かけて築き上げた「強整合性を維持しながら、地球規模でスケールする」という矛盾を物理法則の限界まで追い込んで解決した、現代分散システムの到達点だ。

多くのエンジニアがRDBの感覚で設計し、後に地獄を見る。Spannerで「正しく」テーブルを定義し、型を選ぶことは、単なるデータの箱作りではない。それは、あなたのアプリケーションのパフォーマンスという名の物理法則を制御する行為だ。

—

1. データ型:なぜ「型選び」がスループットを左右するのか

Spannerの型システムは標準的だが、その裏にある「符号化」と「インデックス」の挙動を理解しなければならない。

実務で刺さる型の選定基準

  • STRING vs BYTES: `STRING`はUTF-8エンコードされる。もし単なるIDやハッシュ値なら`BYTES`を使え。ストレージ容量を抑え、比較コストを下げることで、広大なスキャン範囲でのパフォーマンスが変わる。
  • INT64 vs NUMERIC: 金額計算には迷わず`NUMERIC`を使え。`FLOAT64`は精度が重要なビジネスロジックには適さない。
  • JSON型: 柔軟性は麻薬だ。スキーマレスなJSONを多用すれば、インデックス設計が破綻し、クエリの実行計画が予測不能になる。「検索の必要がないメタデータ」のみをJSONに閉じ込めるのが賢者の設計だ。
  • ARRAY: これこそがRDBと一線を画す機能だ。`ARRAY`を使えば、JOINを回避して「1対多」の関連を1行に収められる。JOINはSpannerにおける最大のコスト要因だ。 ARRAYで物理的な局所性を確保せよ。

—

2. テーブル定義の極意:プライマリキーこそが「生命線」

Spannerのテーブル設計で最も重要なのはプライマリキー(PK)の選択だ。ここを間違えると、データは特定のノードに偏り(Hotspotting)、Spannerは単なる高価な遅いDBに成り下がる。

避けるべき設計(アンチパターン)

  • 単調増加する値(シーケンスやタイムスタンプ)をPKの先頭にする:

これを行うと、すべての書き込みが「最新のキーを持つノード」に集中する。地球規模の分散パワーを、たった1つのノードで受け止める無意味な行為だ。

  • uuid v4(ランダム)の採用:

書き込みの分散には効くが、範囲クエリの効率が落ちる。

推奨設計:ハッシュ化されたキーの分散

UUIDの前半をハッシュ化して分散させるか、あるいは「ビジネスロジック的に意味のある階層」を意識した複合キーを設計せよ。

— 推奨されるテーブル定義の例
CREATE TABLE Users (
— 分散を考慮したキー設計 (Shard ID + UserID)
ShardId INT64 NOT NULL,
UserId STRING(36) NOT NULL,
UserName STRING(MAX),
Profile JSON,
Tags ARRAY, — 関連情報をARRAYで持ち、JOINを回避
CreatedAt TIMESTAMP OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (ShardId, UserId);

—

3. インターリーブ(Interleaving): 物理的な局所性を支配せよ

Spannerの真価は、親テーブルと子テーブルを物理的に同じノードに格納する「インターリーブ」にある。

親テーブル(例: `Customers`)と子テーブル(例: `Orders`)をインターリーブ設定すると、`Customers`の行のすぐ隣にその顧客の`Orders`が配置される。これにより、親子関係の読み取りにおいてネットワークホップをゼロにできる。

— 子テーブルを親にインターリーブさせる設計
CREATE TABLE Orders (
CustomerId STRING(36) NOT NULL,
OrderId STRING(36) NOT NULL,
Amount NUMERIC,
) PRIMARY KEY (CustomerId, OrderId),
INTERLEAVE IN PARENT Customers ON DELETE CASCADE;
— 物理的にCustomersの下に配置される。クエリ性能が劇的に改善する。

—

4. チーフアーキテクトからの忠告

1. 「とりあえずJOIN」は悪: Spannerにおいて、異なるノードにまたがるテーブルのJOINはネットワーク遅延を生む。データモデルを非正規化してでも、読み取り性能を優先すべき局面があることを忘れるな。
2. インデックスは控えめに: セカンダリインデックスは書き込み性能を確実に低下させる。インデックスを追加するたびに、そのインデックスの更新コストがトランザクションのレイテンシにどう影響するかを想像しろ。
3. コミットタイムスタンプを活用せよ: `OPTIONS (allow_commit_timestamp=true)` を使え。アプリケーション側で時刻を生成するな。Spannerの真時刻(TrueTime)を信頼し、順序保証をデータベースに委譲するのが最も堅牢だ。

最後に

Cloud Spannerは、エンジニアの「サボり」を許さないDBだ。しかし、物理配置を深く理解し、それに基づいた設計を行えば、あなたのシステムは世界中のどこからアクセスされても、一貫した強整合性と圧倒的なパフォーマンスを維持する。

次の設計レビューでは、「なぜそのキーを選んだのか」「なぜそのデータ型なのか」を物理層まで落とし込んで説明できるようにしてほしい。それが、世界最高峰のエンジニアへの第一歩だ。

コメント

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