【正解】D
【0からの解説】
①前提知識:Spanner は主キーの値の範囲(キー範囲)ごとにデータを「スプリット」に分割し、複数のサーバーに分散して処理する分散データベースです。そのため、1, 2, 3, … のように単調に増加する値を主キーにすると、新しい行の書き込みが常にキー範囲の末尾(同じスプリット)に集中し、1 台のサーバーに負荷が偏る「ホットスポット」が発生します。これを避けるため Spanner には、内部の連番カウンタをビット反転してから返す「ビット反転シーケンス(bit-reversed sequence)」が用意されています。PostgreSQL 言語のデータベースでは
CREATE SEQUENCE ... BIT_REVERSED_POSITIVE で作成し、列のデフォルト値に
nextval('seq') を指定できます。生成される値は正の 64 ビット整数(bigint)で、キー空間全体に均等に散らばります。
②要件と制約語の整理:(1) 移行元の主キーは「整数」である、(2) アプリケーションの変更を「最小限」にしたい、(3) Google 推奨のプラクティスに従う(=ホットスポットを作らない主キー設計)、の 3 点です。
③正解の理由:Spanner の「主キーを移行する」ドキュメントでは、移行元で AUTO_INCREMENT / SERIAL / IDENTITY などで自動採番された整数キーについて、Spanner のシーケンス(ビット反転シーケンス)を列のデフォルト値として作成し、既存行は元のキー値のまま移行し、既存の値と衝突しないよう
ALTER SEQUENCE で使用済みの範囲をスキップする(SKIP RANGE)か開始カウンタを既存値より大きくする方法が説明されています。主キーの型は整数のまま、既存の値も変わらず、アプリは INSERT でキー列を省略するだけで新しい値が自動採番されるため、アプリ側の変更が最小で済みます。同時にビット反転によって値が分散するのでホットスポットも回避でき、Google 推奨のプラクティスにも合致します。
【誤りの選択肢】
A:アプリケーションで連番の整数を生成すると、新しい行が常にキー範囲の末尾に書き込まれ、Spanner で典型的なホットスポットになります。さらに、採番ロジックをアプリ側に実装・維持する必要があり、複数のアプリ インスタンス間で重複しないよう調整する仕組みも必要になります。Google が明確に避けるよう推奨している設計です。
B:max(primary_key) + 1 で次のキーを決める方法は、やはり単調増加の値になるためホットスポットを生みます。加えて、挿入のたびに最大値を読み取る必要があり、同時実行される挿入どうしでロック競合やキー重複が発生しやすく、スケーラビリティも性能も劣ります。
C:UUID バージョン 4 は値がランダムに分散するため、Spanner でも推奨される主キーの選択肢の 1 つです。しかし UUID は文字列(STRING / text)型であり、既存の整数型の主キーを置き換えることになるため、主キーの型変更・既存データの変換・外部キーや参照側の列の変更、アプリケーションでのキーの扱いの変更が必要になります。「アプリケーションへの変更を最小限に」という制約に対しては、整数型のまま使えるビット反転シーケンス(D)の方が適しています。
【参考】
主キーを移行する | Spanner主キーのデフォルト値管理 | Spannerスキーマ設計のベスト プラクティス | Spanner