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

やあ、最近どう? クエリのパフォーマンスで頭を抱えてないかい?

PostgreSQLを使っていて「クエリが遅いな」と感じたとき、真っ先に思い浮かぶのは「とりあえずインデックスを貼る」ことだと思う。でも、ただインデックスを貼るだけじゃ到達できない「極致」があるんだ。それがインデックスオンリースキャン(Index Only Scan)だ。

今日は、クエリチューニングの「最後の一押し」とも言えるこの技術について、現場の知見を交えて話していくよ。

—

インデックスオンリースキャンとは何か?

通常のインデックススキャンは、インデックスを使って行の場所(ポインタ)を特定し、その後に「テーブル本体(ヒープ)」へデータを取りに行く。これが基本の動きだ。

でも、考えてみてほしい。「もしインデックスの中に必要なデータが全部揃っていたら、わざわざ重たいテーブル本体を見に行く必要はないんじゃないか?」

そう、インデックスだけで完結させる。これがインデックスオンリースキャンだ。ディスクI/Oを劇的に減らせるから、爆速になる。

—

魔法の鍵:Visibility Map(可視性マップ)

ここで一つ大きな疑問が湧くはずだ。「インデックスには『その行が現在有効かどうか(可視かどうか)』という情報がないのに、どうやってテーブルを見に行かずに済ませるんだ?」と。

PostgreSQLはここでVisibility Map (VM)という賢い仕組みを使っている。

簡単に言うと、VMは「このページのデータは、データベース内のどのトランザクションから見ても(すべて)有効だよ」という情報を保持しているビットマップだ。

  • もしVMが「このページは全員から見える」と言っていれば、PostgreSQLはテーブルを見に行かず、インデックスの情報だけで「この行は確実に存在する」と判断できる。
  • 逆にVMが「まだ変更中かも」と言っていれば、結局テーブルに確認しに行くしかない。

つまり、「更新頻度が低く、読み取りが多いテーブル」ほど、この最適化が効きやすいということだね。

—

実践:どうやって狙い撃つか

理論はわかったと思う。じゃあ、実戦ではどうするか。

例えば、ユーザーのメールアドレスを検索するクエリを考えてみよう。

— よくある構成
CREATE TABLE users (
id serial PRIMARY KEY,
username text,
email text,
created_at timestamp
);

CREATE INDEX idx_users_email ON users(email);

この状態で以下のクエリを投げるとする。

EXPLAIN (ANALYZE, BUFFERS)
SELECT email FROM users WHERE email = ‘test@example.com’;

もしインデックスに `email` しか含まれていなければ、PostgreSQLは `email` を取得するためにテーブルを見に行く必要がある。でも、ここで「INCLUDE句」を使うと世界が変わる。

— 既存のインデックスを再定義
CREATE INDEX idx_users_email_covering ON users(email) INCLUDE (id);

こうすると、`email` で検索して、かつインデックスの中に `id` が含まれているから、クエリの結果をインデックスだけで完結させられるんだ。「カバリングインデックス」なんて呼ばれ方もするね。

—

現場で気をつけるべき「落とし穴」

インデックスオンリースキャンは強力だけど、銀の弾丸じゃない。以下の点には注意してくれ。

1. VACUUMが鍵を握る: 先述のVisibility Mapは、VACUUMが走った時に更新される。`autovacuum` が動いていないテーブルだと、いつまで経ってもインデックスオンリースキャンが発動しないことがある。「最近クエリが遅くなった」と思ったら、まずは統計情報とVACUUMの状態を確認してくれ。
2. インデックスを増やしすぎない: 「全部インデックスに入れればいいじゃん!」と思うかもしれないけれど、インデックスが増えれば書き込み(INSERT/UPDATE)のコストが跳ね上がる。トレードオフを忘れないように。
3. `EXPLAIN` を信じろ: 自分の頭で考えるのは大事だけど、最後は必ず `EXPLAIN (ANALYZE, BUFFERS)` を実行して、本当に `Index Only Scan` が選択されているか、`Heap Fetches` が0になっているか確認する癖をつけよう。

—

最後に

インデックスオンリースキャンは、データベースの内部構造を理解したエンジニアだけが扱える「職人芸」に近い。

最初は難しく感じるかもしれないけれど、`EXPLAIN` の結果を見ながら試行錯誤して、I/Oを極限まで減らせたときの爽快感は格別だぞ。

もし「インデックスを貼ったのにクエリが遅い!」と嘆いている後輩がいたら、この記事を教えてやってくれ。現場からは以上だ!

コメント

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