【実務・中級編】 インデックスオンリースキャン – PostgreSQL

「インデックスを貼ったのに遅い?」を卒業する。PostgreSQLのインデックスオンリースキャンを使いこなそう

「インデックスを貼ればクエリは速くなる」。これはデータベースエンジニアの基本中の基本ですよね。でも、実務でSQLをチューニングしていると、「インデックスをちゃんと使っているはずなのに、なぜか期待したほど速くならない」という壁にぶつかることはありませんか?

特に、何万、何百万というレコードを扱うテーブルで、少しでもレスポンスを削り出したいとき。そんな時に武器になるのが、今回紹介する「インデックスオンリースキャン(Index Only Scan)」です。

今日は、この「最強の最適化手法」の仕組みと、現場でどう意識して設計すべきか、少し深掘りしてみましょう。

—

1. なぜ「インデックス」だけでは足りないのか?

普通、PostgreSQLがインデックスを使ってデータを検索する流れはこうです。

1. インデックスを辿って、目的の行の「物理的な場所(ポインタ)」を見つける。
2. そのポインタを頼りに、テーブルの本体(ヒープ)まで行って、実際のデータを読み出す。

この「2. テーブルの本体を見に行く」という作業が、実は結構なコストなんです。特に大量の行をフェッチする場合、ディスクI/Oがボトルネックになってしまいます。

インデックスオンリースキャンは、文字通りこのステップを省きます。インデックスの情報だけでクエリを完結させる。つまり、テーブル本体を一度も見に行かない(=I/Oが発生しない)という、究極のショートカットなんです。

2. 鍵を握る「可視性マップ(Visibility Map)」の正体

ここで疑問に思うはずです。「インデックスには実データがないこともあるのに、どうやって中身を保証しているの?」と。

その答えが、PostgreSQLの可視性マップ(Visibility Map)です。

PostgreSQLは、あるページ(ブロック)内のすべての行が「全トランザクションから見て可視である(最新である)」ことを知っています。この情報を管理しているのが可視性マップです。

もしインデックスに欲しいカラムが含まれていて、かつ可視性マップを見て「このページ内のデータはみんな最新だよね」と確認できれば、わざわざヒープを見に行かなくても、インデックスのデータだけでクエリを確定できる。これがインデックスオンリースキャンの仕組みです。

3. 実践:インデックスオンリースキャンを狙い撃つ

理屈はわかったところで、どうやって設計に落とし込むか。一番簡単なのは「INCLUDE句」の活用です。

例えば、ユーザーのメールアドレスとステータスを頻繁に検索するようなケースを考えてみましょう。

— よくあるクエリ
SELECT email FROM users WHERE status = ‘active’;

単に `status` にインデックスを貼るだけでは、`email` を取得するために結局ヒープを見に行きます。そこで、こうします。

CREATE INDEX idx_users_status_email ON users (status) INCLUDE (email);

こうすると、`status` で検索をかけつつ、インデックスの中に `email` のデータも埋め込まれるので、クエリはインデックスだけで完結します。これが「カバリングインデックス」という考え方ですね。

4. チューニングの注意点:VACUUMが鍵を握る

さて、ここで一つ、現場でハマりやすいポイントを伝授しておきます。

インデックスオンリースキャンは「可視性マップ」に依存しているため、データが頻繁に更新(UPDATE)されるテーブルだと、思ったように発動しないことがあります。更新直後は可視性マップが「まだ読み込まれていない」と判断されるためです。

もし「インデックスオンリースキャンが効かないな?」と思ったら、`VACUUM` が適切に走っているか確認してください。AutoVacuumの設定が甘いと、可視性マップが更新されず、この最適化が使われないケースは非常に多いです。

まとめ:エンジニアとして意識すべきこと

インデックスオンリースキャンは、魔法ではありません。

  • SELECTするカラムを絞る: `SELECT ` を封印し、必要な列だけを指定する。
  • カバリングインデックスを検討する: `INCLUDE` 句を使い、インデックスにデータを抱え込ませる。
  • 運用を考える: VACUUMが正常に機能する環境を作る。

この3つを意識するだけで、あなたのクエリの実行計画(`EXPLAIN`)の表記は、`Index Scan` から `Index Only Scan` に華麗に変わるはずです。

データベースは嘘をつきません。仕組みを理解して設計してあげれば、必ずパフォーマンスで応えてくれます。皆さんの現場のクエリも、ぜひ一度 `EXPLAIN ANALYZE` してみてください。きっと、まだ削れる無駄が見つかるはずですよ。

それでは、また次回のチューニングでお会いしましょう!

コメント

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