インデックス・バックフィルという名の「見えざる戦い」:Spannerの深淵
多くのエンジニアが「Spannerのインデックス追加はオンラインで実行できる魔法だ」と信じている。だが、大規模システムを設計する我々にとって、その裏で何が起きているかを理解することは、単なる仕様把握ではなく「生存戦略」そのものだ。
本稿では、`ALTER TABLE ADD INDEX`を発行した瞬間、Spannerの分散環境で何が物理的に起きているのか、その「バックフィル(Backfill)」の深層を解き明かす。
—
1. バックフィルは「分散トランザクションの極致」である
Spannerにおいてインデックスの作成は、単なるメタデータの変更ではない。既存の数十億行に及ぶデータセットを走査し、新しいインデックス・キーを生成し、それを適切なスプリット(Split)へ分散配置する「大規模並列処理」だ。
このプロセスは、以下のフェーズで構成される。
1. State Transition: インデックスは最初 `WRITE_ONLY` 状態で作成される。この時点では読み取りには使用されないが、書き込みは反映される。
2. Distributed Backfill: 既存データのスキャンとインデックス・エントリの作成。
3. Finalization: `READ_WRITE` への昇格。
ここで重要なのは、このバックフィルが複数のスプリットに対して非同期的に並列実行されるという点だ。Spannerの各ノードは、自身の管轄するキーレンジに対して読み取りを行い、インデックス・テーブルへ書き込む。この際、通常のトランザクションと競合しないよう、内部的な優先度制御とthrottlingが行われている。
2. アーキテクチャの限界:なぜ「時間がかかる」のか
バックフィルの完了時間は、単純なデータ量に比例しない。以下の変数が複雑に絡み合う。
- スプリットの密度: データの物理的な配置が断片化していれば、スキャン効率は低下する。
- CPU負荷: バックフィル処理は、ユーザーのトラフィックと同じCPUリソースを奪い合う。Spannerのスケジューラは、ユーザーのクエリ実行を最優先するため、負荷が高い時間帯にバックフィルを投げれば、バックフィルは「飢餓状態」に陥る。
- トランザクションの競合: バックフィル中に該当テーブルへの更新が頻発すると、ログの書き出しやメタデータの同期コストが指数関数的に増大する。
極限の知見:バックフィルの「完了」をどう見積もるか
多くの者が `Operation` APIの進捗率を眺めるが、あれはあくまで目安だ。真の完了は、「最新のデータがインデックスに完全に同期された瞬間」であり、Spannerはその一貫性を担保するために、分散コミットメントの最後の一手を慎重に打つ。
もし、数億行のテーブルでバックフィルが数分で終わると考えているなら、それは幻想だ。数時間に及ぶこともある。重要なのは、「いつ終わるか」ではなく「システム全体のパフォーマンスを破壊せずにバックフィルを完了させるか」という戦術だ。
3. 実務レベルの最適化:エンジニアができる唯一のこと
バックフィル実行中に、システムを破綻させないための「伝説的」な知見を共有しよう。
A. トラフィックの非対称性
バックフィルはCPUリソースを消費する。ピークタイムに実行するのは愚策だ。しかし、オフピークであっても、大規模なDDLは「バックグラウンド・タスク」として優先順位が下げられる仕様になっている。あえてトラフィックが皆無の深夜を狙うより、「システムのキャパシティに20〜30%の余裕がある時間帯」を選ぶのが正攻法だ。
B. インデックス・インテグリティの観察
バックフィルの進捗を確認する際は、`sys.operations` を直接叩く。
— 現在実行中のバックフィル操作を特定する
SELECT
name,
metadata,
done
FROM
sys.operations
WHERE
type = ‘type.googleapis.com/google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata’;
— 戻り値の metadata フィールドから progress_percentage を抽出する
— 注意: このパーセンテージは見積もりに過ぎない。
C. 避けるべきアンチパターン
- 短期間の連続DDL: 複数のインデックスを同時に追加しようとするな。バックフィルが重なり合い、CPUの飽和とデッドロックのリスクが跳ね上がる。一つずつ実行し、`READ_WRITE` 状態への遷移を確認してから次へ進むのが鉄則だ。
- 巨大なテーブルへのインデックス追加: 数TBクラスのテーブルで、インデックスのキーが複雑(型変換を伴う計算フィールドなど)な場合、バックフィルの負荷は劇的に増す。可能な限り、インデックスのデータ型をシンプルに保て。
—
結びに:データベースを支配する者へ
Cloud Spannerにおけるインデックス・バックフィルは、静かなるバックグラウンド処理だが、その裏側にはSpannerの分散トランザクションエンジンが総力を挙げて取り組む「巨大な再構成」がある。
「なぜ時間がかかるのか?」「なぜリソースを食うのか?」
この問いに対する答えを、単なるエラーログやドキュメントではなく、Spannerという分散システムの本質的な挙動から導き出せる者だけが、この巨大なデータベースを真に制御できる。
インデックスを追加するということは、既存の物理レイアウトに手を加えるという、最も重い責任を伴う行為である。その重さを理解した上で、慎重かつ大胆に実行せよ。それが、真のアーキテクトの矜持だ。
コメント