Cloud Spannerの「NULL」を制する者は、分散データベースの深淵を制する
設計レビューでよく見る光景がある。「とりあえずNULL許容にしておけば後で困らないだろう」という判断だ。だが、Cloud Spannerという極めて洗練された分散データベースにおいて、その甘い考えはパフォーマンスと運用コストの両面で痛いしっぺ返しを食らうことになる。
今日は、SpannerにおけるNULLの正体を暴き、我々が取るべき「プロフェッショナルな設計」について話をしよう。
—
1. SpannerにおけるNULLの哲学:それは「不在」ではなく「未定義」
多くのエンジニアが誤解しているが、SpannerのNULLは「0」でも「空文字」でもない。「値が存在しない」という特定の状態だ。
Spannerの内部アーキテクチャにおいて、NULLはカラムのデータ型とは独立したメタデータとして扱われる。データがNULLの場合、Spannerのストレージ層ではそのカラムに実データを書き込まない。つまり、NULLはストレージ容量を消費しないという利点がある。
しかし、この「存在しない」という事実は、クエリ実行時やインデックス設計において劇的な挙動の変化をもたらす。
—
2. インデックスとNULLの「危険な関係」
ここが最も重要だ。設計者は、インデックス作成時にNULLがどう扱われるかを脳に焼き付けておく必要がある。
稀なインデックス(Sparse Index)という戦略
Spannerのインデックスは、NULL値も格納する。しかし、もし特定のクエリで「NULL以外の値だけを頻繁に検索する」という要件があるなら、`WHERE column IS NOT NULL` をインデックスの述語に組み込むべきだ。
— 悪い例: 全レコードをインデックスに含めてしまう
CREATE INDEX idx_user_email ON Users(email);
— 良い例: NULLを除外することでインデックスのサイズを削減し、スキャン効率を最大化する
CREATE INDEX idx_user_email_active ON Users(email) WHERE email IS NOT NULL;
なぜこれが重要か?
インデックスはキーの順序を保持する構造だ。NULLが大量に含まれるインデックスは、範囲検索のパフォーマンスを劣化させ、不要なストレージ消費を招く。NULLが「ビジネス的に無意味なデータ」であるならば、迷わず除外インデックス(Filtered Index)を検討せよ。
—
3. クエリにおける「NULLの罠」
SQLの三値論理(TRUE, FALSE, UNKNOWN)を忘れていないか?
特に、アプリケーション側で「NULLならデフォルト値を返したい」といったロジックを組む際、`WHERE` 句でのNULL比較を忘れると、予期せぬデータ漏れが発生する。
— よくあるバグ: status が NULL のレコードは、この条件からは漏れる
SELECT FROM Orders WHERE status != ‘COMPLETED’;
— 修正案: COALESCEでNULLを安全な値に変換する
— ただし、関数を通すとインデックスが効かなくなる可能性があるため注意が必要
SELECT FROM Orders WHERE COALESCE(status, ‘PENDING’) != ‘COMPLETED’;
チーフアーキテクトからの助言:
可能な限り、データベース設計段階で `NOT NULL` 制約を強制することを推奨する。アプリケーション側で「NULLのまま投げ込む」のは技術的負債の先送りだ。ビジネスロジックで表現できる「デフォルト値(例: ‘N/A’, 0, 1970-01-01)」を定義した方が、分散クエリの最適化エンジンにとっても遥かに都合が良い。
—
4. プロフェッショナルな設計判断:NULLを使うべき時、避けるべき時
実務における指針を提示する。
NULLを使うべきケース
- 疎なデータ構造: 多くのレコードで値が埋まらないオプション的な属性(例:ユーザーの備考欄など)。ストレージ効率の観点からNULLが合理的だ。
- 履歴データ: 終了日時など、まだ発生していないイベントを表現する場合。
NULLを避けるべきケース
- プライマリキーの一部: 言わずもがなだが、キーの一部をNULLにするのは設計の破綻だ。
- 頻繁にフィルタリングするカラム: 前述の通り、クエリ実行計画を複雑にし、オプティマイザの判断を鈍らせる。
- 結合キー: `JOIN` 句においてNULLが含まれると、結合のパフォーマンスが予測不可能になる。
—
最後に:謙虚であれ、厳格であれ
Cloud Spannerは、デフォルトで多くのことを自動的に最適化してくれる。しかし、NULLの扱いに関しては「データベースが勝手にやってくれる」という甘えは許されない。
NULLを許容するということは、そのフィールドの「状態が未定義であること」をシステムが許容するということだ。その設計が、将来の検索クエリの実行速度や、アプリケーションの例外ハンドリングにどう影響するか。コードを書く前に、一度立ち止まって考えてみてほしい。
「NULLを制する設計」は、システムに堅牢さと予測可能性をもたらす。それが、我々エンジニアが追求すべきプロフェッショナリズムの形だ。
—
次回のレビューでは、このNULL戦略に基づいた設計図を見せてもらう。期待しているぞ。
コメント