【実務・中級編】 クエリにおけるインデックス選択 – Cloud Spanner

Cloud Spannerのインデックス選択:オプティマイザを「手懐ける」技術

Cloud Spannerは、分散データベースにおける「一貫性と可用性の両立」という聖杯を、真の意味で手に入れたシステムだ。しかし、この高度な分散環境において、多くのエンジニアが陥る罠がある。それは「オプティマイザへの過信」と「無闇なインデックス操作」だ。

今日は、Spannerのクエリ実行エンジンがどう考え、どう動くのか。そして、なぜ「FORCE INDEX」という禁じ手が存在するのか、その深淵に触れる。

—

1. オプティマイザの「思考」を理解せよ

Spannerのクエリ実行エンジンは、コストベースオプティマイザ(CBO)だ。統計情報(Row Count、分布、カラムのカーディナリティなど)を基に、複数の実行プランを評価し、最も「コストが低い」と判断したものを選択する。

しかし、ここで忘れてはならないのは、統計情報は常に「少しだけ過去の真実」であるという点だ。データが急激に偏ったとき、あるいは統計情報が更新されるまでのタイムラグがあるとき、オプティマイザは最善手を見誤ることがある。

インデックス選択の優先度

オプティマイザは通常、以下の順序に近いヒエラルキーでインデックスを評価する。
1. 主キー(PK)検索: 最も安価。Direct Look upが効く。
2. カバリングインデックス: クエリに必要な全カラムが含まれていれば、テーブル本体への読み取り(Join)を回避できるため、極めて強力。
3. セカンダリインデックス: フィルタリングには有効だが、検索対象外のカラムが必要な場合、`Base Table`への`Back-join`が発生する。このコスト計算こそが、オプティマイザの最大の腕の見せ所だ。

—

2. FORCE INDEX:救世主か、それとも諸刃の剣か

Spannerには`FORCE INDEX`(`@FORCE_INDEX`ヒント)が存在する。だが、設計レビューでこれが出てきたら、私はまず「なぜオプティマイザがそれを選ばなかったのか?」と問う。

FORCE INDEX を使うべき正当な理由

  • 統計情報の過渡期: 大規模なデータロード直後など、統計が実態と乖離している間の暫定的な対応。
  • 特定のクエリパターン: 常に特定のインデックスを通すことがビジネスロジック的に確実なパフォーマンスを保証する場合。

絶対にやってはいけないこと

  • 「なんとなく速そうだから」という理由での固定: データボリュームが増大したとき、そのインデックスはボトルネックに化ける。
  • 開発環境のデータ量で判断: Spannerのインデックス効率は、数万行と数億行では全く別の挙動を見せる。

—

3. 実践:クエリの「質」を高める設計パターン

インデックスを選択させるために重要なのは、ヒントを無理やり押し付けることではなく、オプティマイザが迷わないクエリを書くことだ。

パターンA:カバリングインデックスの最大活用

単なるフィルタリングだけでなく、`STORING`句を使って必要なカラムをインデックス側に持たせる。これができれば`Back-join`は不要になり、実行コストは劇的に下がる。

— 悪い例:インデックスはあるが、Back-joinが発生する
— 良い例:STORING句で必要なカラムを含める
CREATE INDEX UsersByEmail ON Users (Email) STORING (DisplayName, LastLoginAt);

— これにより、以下のクエリはインデックスだけで完結する(Index Only Scan)
SELECT DisplayName, LastLoginAt
FROM Users@{FORCE_INDEX=UsersByEmail}
WHERE Email = ‘dev@example.com’;

パターンB:述語の最適化

`WHERE`句で計算を行ったり、関数を通したりすると、インデックスが効かない(SARGableでない)状態になる。

— 悪い例:関数を通すとインデックスが使われない
WHERE LOWER(Email) = ‘dev@example.com’

— 良い例:アプリケーション側で正規化してからクエリを投げる
WHERE Email = ‘dev@example.com’

—

4. チーフアーキテクトからの提言

実務において、パフォーマンス問題の9割はインデックスの不足ではなく、「クエリの設計思想」に起因する。

1. 実行プランを確認せよ: `EXPLAIN ANALYZE`を叩かないエンジニアに、Spannerを扱う資格はない。必ずリクエストごとの`Execution Plan`を確認し、`Index Scan`が想定通りか、`Distributed Union`がどこで起きているかを見極めろ。
2. インデックスはコスト: インデックスは書き込み性能(Mutation)を確実に劣化させる。読み取りの速度と書き込みのコストのトレードオフを、ビジネスの優先順位に基づいて天秤にかけるのだ。
3. 迷ったら「FORCE」の前に「SCHEMA」を見直せ: `FORCE INDEX`を強要する設計は、将来の拡張性を阻害する。インデックスが必要なら、まずはクエリの要求仕様とスキーマ設計が適合しているかを疑え。

Spannerは高度に抽象化されたデータベースだが、物理的な制約からは逃れられない。オプティマイザを信じるな、しかし利用せよ。統計を理解し、クエリを磨き上げ、データと対話する。それこそが、Cloud Spannerを使いこなす唯一の道だ。

次のレビューでは、`FORCE INDEX`のコードではなく、その背後にある「なぜそう設計したのか」という論理的な考察を期待している。健闘を祈る。

コメント

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