インデックスの「読み方」を変える:Index ScanとBitmap Index Scanの境界線
「なぜ、このインデックスが使われないんだ?」
DBエンジニアなら一度は頭を抱える瞬間ですよね。特に、あるクエリでは爆速なのに、似たような別のクエリではIndex ScanではなくBitmap Index Scanが選ばれて、パフォーマンスが伸び悩む……そんな経験はないでしょうか。
今日は、PostgreSQLにおけるこの2つのスキャンの「正体」と、なぜオプティマイザがBitmap Scanという少し回りくどそうな方法を選ぶのか、その裏側の物語を解説します。
—
Index Scan:直球勝負の職人技
まずは基本のIndex Scanから。
これは非常にシンプルです。「インデックスを辿って、特定のレコードの物理的な位置(TID: Tuple ID)を特定し、そのままデータページへ一直線に読みに行く」という方法です。
— IDが100のユーザーを探す
SELECT FROM users WHERE id = 100;
このとき、PostgreSQLはインデックスを木構造(B-Tree)として辿り、ターゲットの場所を即座に見つけます。ピンポイントで「あそこのページだ!」と特定できるため、オーバーヘッドが極めて少ない。これが理想的な動作です。
Bitmap Index Scan:効率的な「まとめ読み」の魔術
では、Bitmap Index Scanはなぜ登場するのでしょうか。
想像してみてください。インデックスを使って1,000件のレコードを探すとき、もしその1,000件がテーブル内のバラバラのページに散らばっていたらどうなるでしょう?
1. インデックスから場所を特定する
2. ページを読みに行く(ディスクI/O発生!)
3. 次のレコードのために別のページを読みに行く(またI/O!)
これを繰り返すと、ディスクのヘッド(HDDの場合)やキャッシュのヒット率の観点で、「ランダムアクセス」の嵐に巻き込まれます。これがIndex Scanの最大の弱点です。
そこでBitmapの出番です。
1. Bitmap Index Scan: インデックスをスキャンして、条件に合うレコードの場所を「ビットマップ(0と1の地図)」に書き出す。
2. Bitmap Heap Scan: その地図を元に、ページ番号順にソート(または整理)してから、効率的にテーブルを読みに行く。
つまり、「あちこち飛び回る」のではなく、「必要なページをルート順に効率よく回収する」ためのバッファリングなんです。
—
どんな時にBitmap Scanが選ばれるのか?
オプティマイザは、以下のような基準でBitmap Scanを「採用」します。
- 取得件数が多いとき: インデックスを一つずつ辿るよりも、ビットマップにまとめてから一気に読んだ方が、ページアクセスの重複(同じページを何度も読み込む無駄)を避けられる。
- クエリの条件が複雑なとき: `WHERE status = ‘active’ AND type = ‘premium’` のように複数のインデックスを組み合わせる(Bitmap And / Or)必要があるとき。
特に、「インデックスは効いているはずなのに、なぜかBitmap Scanになって遅い」と感じる場合、そのクエリが「テーブルの大部分を読み込もうとしていないか?」を疑ってください。
—
実践的なアドバイス:チューニングの現場から
もし皆さんの環境で「Bitmap Scanが多発していて遅い」というクエリがあれば、インデックスを増やす前に、まずは以下の2点を確認してみてください。
1. 取得するカラムを絞る:
`SELECT ` で全カラム取っていませんか? 必要なカラムだけを取るようにし、そのカラムがインデックスに含まれていれば「Index Only Scan」に昇格できるかもしれません。
2. インデックスの選択性を疑う:
「status = ‘active’」のように、値の種類が少ないカラムにインデックスを貼っていませんか? 選択性が低いと、PostgreSQLは「結局テーブルの半分を読みに行くことになるから、普通のIndex ScanよりBitmap Scanの方が安全だ」と判断しがちです。
まとめ:敵ではない、味方だ
Bitmap Index Scanは、決して「遅いスキャン」ではありません。「ランダムアクセスのコストを最小化するための、賢い防衛策」です。
もしこれが選ばれているなら、それは「ランダムアクセスが多すぎて、素直にインデックスを辿ると地獄を見る」とDBが悲鳴を上げているサインかもしれません。その時はクエリの書き方や、複合インデックスの作成を検討するタイミングです。
データベースの裏側の挙動が見えてくると、チューニングはパズルみたいに面白くなりますよ。さて、次はどのクエリを最適化しましょうか?
コメント