Cloud Spannerにおける「外部キー」の真実 — 分散データベース時代の整合性設計
エンジニア諸君、設計レビューお疲れ様。
今日はCloud Spannerにおける「外部キー制約(Foreign Key Constraint)」について話そう。
多くのエンジニアが、RDBMSの感覚で安易に外部キーを貼る。しかし、Cloud Spannerは「分散データベース」だ。ローカルのPostgreSQLとは物理的な制約が全く異なる。この特性を理解せずに設計すると、スケールアウトの瞬間にシステムは悲鳴を上げることになる。
今回は、Spannerという巨大なエンジンの上で、いかに堅牢かつ高性能なデータ整合性を担保するか、その「極意」を伝授する。
—
1. なぜSpannerで外部キーを使うのか?
Spannerの外部キーは、単なる「お守り」ではない。データライフサイクルの管理機構だ。
Spannerは厳密な外部キー制約をサポートしている。これは、`INSERT`や`UPDATE`のたびに、参照先のテーブルに対して内部的な読み取り整合性チェックが行われることを意味する。
実装例:UsersとOrdersの紐付け
CREATE TABLE Users (
UserId INT64 NOT NULL,
Name STRING(MAX),
) PRIMARY KEY (UserId);
CREATE TABLE Orders (
OrderId INT64 NOT NULL,
UserId INT64 NOT NULL,
Amount INT64,
— 外部キー定義
CONSTRAINT FK_Orders_Users FOREIGN KEY (UserId) REFERENCES Users (UserId)
) PRIMARY KEY (OrderId);
ここまでは基本だ。だが、ここからがプロの設計の分かれ道だ。
—
2. ON DELETE CASCADE の「諸刃の剣」
`ON DELETE CASCADE`は、親テーブルのレコード削除時に、子テーブルのレコードを自動削除してくれる強力な機能だ。コード量を減らし、整合性を自動担保できる。
しかし、トラフィックが高いシステムでこれを多用してはならない。
- なぜ危険なのか:
親レコードを1つ消すと、Spannerは内部的にトリガーを引いて、紐づく何千・何万という子レコードを削除しに行く。これが単一のトランザクション内で実行されると、ロックの競合(Lock Contention)を誘発し、最悪の場合、その周辺のレコードすべてが書き込み待ち状態になる。
【チーフアーキテクトからの助言】
- 小規模な参照関係: 積極的に使え。コードの美しさを優先せよ。
- 大規模なトランザクション: `ON DELETE CASCADE`は避け、アプリケーション層で「削除フラグ(論理削除)」を立てるか、バッチ処理で非同期に削除する設計にせよ。Spannerで物理削除を多用するのは、大規模環境では悪手だ。
—
3. パフォーマンスとスケーラビリティの最適化
Spannerの外部キーは、単なる制約ではない。「インデックス」の側面も持つ。
外部キーには必ずインデックスを貼れ
Spannerにおいて、外部キー定義は参照先のテーブルへのアクセスを発生させる。もし`Orders.UserId`に対してインデックスが張られていない場合、外部キー制約のチェックのために全スキャンが発生する可能性がある。
- 鉄則: 外部キー列には、必ずインデックスを作成せよ。これはパフォーマンス維持のための必須要件だ。
「インターリーブ(Interleave)」との使い分け
Spannerには「テーブルインターリーブ」という強力な物理配置最適化機能がある。親子関係が明確で、常にセットでアクセスするなら、外部キー制約よりもインターリーブを選択すべきだ。
- 外部キー: 疎な関係、またはビジネスロジック上の整合性が重要な場合。
- インターリーブ: 物理的に同じノードにデータを固め、JOINを爆速化したい場合。
設計判断の基準:
「ユーザーを削除するとき、その注文データも絶対的に不要になるか?」
Yesならインターリーブを検討せよ。Noなら外部キーで緩やかに結合せよ。
—
4. 運用上の注意点:マイグレーションの罠
既存の巨大テーブルに後から外部キー制約を追加する場合、Spannerはテーブル全体を検証する。
- 検証コスト: 数億行のテーブルに対して外部キーを貼ると、バックグラウンドでの検証処理が長時間走り、スループットに影響を与える可能性がある。
- 回避策: `ALTER TABLE`を実行する前に、クエリで整合性が取れていないデータがないか入念にチェックせよ。本番環境で「制約違反により追加失敗」というエラーを出すのは、設計者として恥だと思え。
—
結論:美しく、かつ鋭く設計せよ
Cloud Spannerの外部キー制約は、「システムが崩壊しないための安全装置」だ。しかし、安全装置に頼りすぎてエンジンを過負荷にさせては本末転倒だ。
1. 論理設計を優先せよ: 外部キーは整合性の最後の砦として定義する。
2. ロックを意識せよ: `CASCADE`の連鎖がどの程度のレコード数に波及するか、常に想像力を働かせろ。
3. インデックスを忘れるな: 外部キー列のインデックスは、Spannerのパフォーマンスの生命線だ。
技術はただ使うのではなく、その裏側にある「分散システムとしての挙動」を理解した上で使いこなせ。君たちのコードレビューで、この知見が活かされることを期待している。
さあ、設計に戻ろう。まだやるべきことは山積みだ。
コメント