【実務・中級編】 ビットマップヒープスキャン – PostgreSQL

「インデックスを貼ったのに、なぜかクエリが遅い……」

DBエンジニアなら一度は直面するこの壁。そんな時、`EXPLAIN`の結果を見て「Bitmap Heap Scan」という文字を見つけたことはありませんか?

「ああ、インデックスがうまく効いてないのか」と溜息をつく前に、ちょっと待ってください。実はそのBitmap Heap Scanこそが、PostgreSQLが過酷なディスクI/O環境で生き残るための「賢い生存戦略」なんです。

今日は、このビットマップヒープスキャンが裏で何を考えているのか、なぜあなたのクエリを救おうとしているのかを、現場の視点から紐解いていきましょう。

—

インデックススキャン vs ビットマップヒープスキャン

まず、基本をおさらいしましょう。通常、私たちがインデックスを使うときは「Index Scan」を期待しますよね。これはインデックスを辿りながら、一つずつテーブルの行(ヒープ)にアクセスする手法です。

しかし、もし「検索対象の行がテーブル全体にバラバラに散らばっていたら?」

一つずつインデックスを辿ってディスクを見に行くと、HDDやSSDはヘッドを行ったり来たりさせる「ランダムI/O」の嵐に巻き込まれます。これが、インデックスを使っているのにクエリが遅くなる主犯です。

そこで登場するのが Bitmap Heap Scan です。

ビットマップヒープスキャンの「賢い仕組み」

この手法は、一度にすべてを解決しようとせず、二段構えで動きます。

1. Bitmap Index Scan: インデックスをスキャンして、条件に合う行の場所(タプルID)を特定し、それを「ビットマップ(0と1の地図)」というメモリ上のデータ構造に変換します。
2. Bitmap Heap Scan: メモリ上のビットマップを「物理的な順序(ディスク上の位置)」に並び替えます。そして、ディスクを先頭から順に走査しながら、ビットマップで「1」になっている場所だけを拾い上げます。

つまり、ランダムアクセスをシーケンシャルアクセスに変換するという荒技なんです。

—

実際にどう動いているか見てみよう

例えば、ある会員テーブルで「登録日が特定範囲」かつ「ステータスがアクティブ」という検索をかけるとします。それぞれの条件にインデックスを貼っていても、Bitmap Heap Scanが選ばれることが多いはずです。

— こんなクエリを投げると…
EXPLAIN ANALYZE
SELECT FROM users
WHERE created_at > ‘2023-01-01’ AND status = ‘active’;

このとき、PostgreSQLはこんな判断をしています。

  • 「`created_at`のインデックスで見つけた行」と「`status`のインデックスで見つけた行」をビットマップ上で論理積(AND)をとる。
  • 物理的に近い位置にある行をまとめて読み込む。

これにより、バラバラな位置にある1,000行を、ディスクを何往復もして探すのではなく、ディスクの塊をガサッと読み込むだけで済ませるわけです。これが実務において「I/O負荷を劇的に下げる」正体です。

—

「Bitmap Heap Scan」が多発する時の心構え

もし、チューニング中に常にBitmap Heap Scanが出てきて悩んでいるなら、以下のポイントをチェックしてみてください。

  • work_memの設定は適切か?

ビットマップはメモリ(`work_mem`)上に作成されます。もしビットマップが大きすぎてメモリに収まらなくなると、ディスクへの書き出し(Lossy Bitmap)が発生し、精度が落ちます。複雑なクエリを叩くなら、`work_mem`を少し多めに積むのが定石です。

  • テーブルの断片化(Vaccum)は足りているか?

物理順序を最適化するBitmap Heap Scanといえど、テーブルが物理的にバラバラ(断片化)であれば効果は半減します。`VACUUM ANALYZE`は定期的に、いや、必須で回しましょう。

  • 「本当にインデックスが必要か?」を疑う

Bitmap Heap Scanが多発するということは、オプティマイザが「インデックスだけじゃ絞りきれないな(あるいはランダムI/Oが多すぎるな)」と判断している証拠です。複合インデックスを貼ることで、そもそもビットマップ変換の必要がない「Index Scan」に持っていける可能性がないか、一度構成を見直してみる価値はあります。

—

最後に:ツールとしてのデータベースを使いこなす

「Bitmap Heap Scanが出た=インデックス設計の失敗」なんて単純な話ではありません。むしろ、PostgreSQLが物理的な制約を乗り越えるために気を遣ってくれている、ありがたい機能なんです。

データベースはブラックボックスではありません。`EXPLAIN`という鏡を使って、彼らが裏でどう悩み、どう解決しようとしているのか。その「意思」を読み解けるようになると、パフォーマンスチューニングは格段に面白くなりますよ。

現場からは以上です!また何か詰まったら、いつでも聞いてくださいね。

コメント

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