【実務・中級編】 カバリングインデックスとINCLUDE句 – PostgreSQL

「クエリが遅いからインデックスを貼ったのに、思ったほど速くならない……」

DBエンジニアなら一度は経験する、この「あるある」。実はそれ、インデックスが「テーブル本体(ヒープ)を見に行ってしまっている」のが原因かもしれません。

今回は、PostgreSQLのチューニングにおいて、知っているだけで武器になる「カバリングインデックス」と`INCLUDE`句の話をします。これ、使いこなすとクエリのレスポンスが劇的に変わりますよ。

—

インデックスは「辞書の索引」でしかない

まず、基本のおさらいをしましょう。PostgreSQLでインデックスを貼ると、B-treeインデックスには「キーとなる列」と、その値が格納されている「テーブル上の物理的な場所(ポインタ)」が保存されます。

データベースがクエリを処理する際、インデックスで検索して、そのポインタを頼りにヒープ(実際のテーブルデータ)へデータを読みに行く。これを「ヒープアクセス」と呼びます。

このヒープアクセス、実は結構なコストなんです。特に大量のレコードを処理する場合、いちいちディスクにランダムアクセスするのは、エンジニアとしては避けたいところ。

ここで登場するのが「インデックスオンリースキャン(Index Only Scan)」です。インデックスの中にある情報だけでクエリが完結すれば、ヒープを見に行く必要はありません。爆速ですよね。

「INCLUDE句」という魔法

でも、インデックスに含めたい列が複数あるとき、従来の方法だとインデックスのサイズが肥大化したり、ソート順の都合でインデックスが使いにくくなったりすることがありました。

そこでPostgreSQL 11から導入されたのが`INCLUDE`句です。

具体例を見てみよう

例えば、ユーザー情報テーブル(`users`)で、`email`で検索して`status`を取得したいとします。

SELECT status FROM users WHERE email = ‘hoge@example.com’;

このとき、普通に`CREATE INDEX ON users (email, status);`とすると、`status`もキーの一部として保存されます。これだと、もし`status`のカーディナリティ(値の種類の多さ)が低い場合、インデックスの最適化が上手く効かないことがあります。

そこで、こう書くんです。

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

こうすると、`email`だけが「検索キー」としてインデックスの木構造に使われ、`status`は「付随するデータ」としてリーフノードにそっと保存されます。

これが何をもたらすか?

  • 検索効率はそのまま: `email`の検索速度は低下しません。
  • インデックスオンリースキャン確定: `SELECT`句に必要な列がすべてインデックス内に揃っているため、PostgreSQLは迷わずヒープに触れず、インデックスだけで答えを返してくれます。

実務で使うときの注意点

「じゃあ、なんでもかんでもINCLUDEすればいいじゃん!」と思うかもしれませんが、それは罠です。

1. インデックスサイズに注意: INCLUDEする列を増やせば増やすほど、インデックスの物理サイズは膨らみます。メモリ(`shared_buffers`)に乗らなくなると、結局ディスクI/Oが発生して本末転倒です。あくまで「本当に必要な列だけ」に絞りましょう。
2. 更新頻度とのトレードオフ: INCLUDEしている列が頻繁に更新されると、そのたびにインデックスの書き換えが発生します。書き込み性能への影響も考慮してください。
3. EXPLAIN ANALYZEを信じる: チューニングの鉄則ですが、必ず`EXPLAIN ANALYZE`で「Index Only Scan」になっているか確認してください。もし「Heap Fetches」という数字がゼロでなければ、まだどこかでヒープにアクセスしています。

最後に:先輩からのアドバイス

実務でパフォーマンスチューニングをするとき、僕はまず「どのクエリが一番ヒープを叩いているか」を`pg_stat_user_indexes`などで確認します。

「ここ、あと1列分インデックスにデータがあればヒープに行かなくて済むのに……」という場面に遭遇したら、迷わず`INCLUDE`句の出番です。

魔法のような機能ですが、あくまで「読み取り重視のクエリ」に対する特効薬。正しく使えば、サーバーのCPU負荷もメモリ負荷も驚くほど下がります。ぜひ次のリリース前のテストで試してみてください。

では、また現場でお会いしましょう!

コメント

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