【テクニカル・上級編】 カバリングインデックス(INCLUDE句) – PostgreSQL

なぜ、インデックスは「探す」ためだけにあると信じているのか?――PostgreSQL `INCLUDE` 句の深淵

データベースのパフォーマンスチューニングにおいて、誰もが一度は「Index Only Scan」の甘美な響きに魅了された経験があるはずだ。しかし、現実にはインデックスに含めたいデータが多すぎて、インデックスサイズが肥大化し、逆にメンテナンスコストやI/Oを圧迫する……というジレンマに陥ることは珍しくない。

そんな時、PostgreSQL 11から導入された `INCLUDE` 句は、まさに福音となった。今日は、この「カバリングインデックス」の真髄について、アーキテクチャの裏側まで少し深く掘り下げてみたいと思う。

—

「キー列」と「付加列」の決定的な違い

まず、基本を整理しよう。`CREATE INDEX` の `INCLUDE` 句で指定したカラムは、B-treeインデックスの「キー」としては扱われない。

ここが重要だ。通常のインデックスキーは、ソート順の維持や、`B-tree`のノード内でのバイナリサーチ(`Search`)に使われる。一方で、`INCLUDE` されたデータは、リーフページ(末尾のページ)にのみ格納される「ペイロード」のようなものだ。

  • キー列: インデックスの階層構造(Branchノード)に影響を与え、比較アルゴリズムの対象となる。
  • 付加列 (INCLUDE): 検索の条件には使えないが、リーフページ内に静かに鎮座する。

この設計が意味するのは、「検索の絞り込みには使わないが、結果セットの構築には必要」という、実務で頻出するあのクエリに対する最適解だ。

Index Only Scan を支える「Visibility Map」の役割

カバリングインデックスを語る上で欠かせないのが「Visibility Map」の存在だ。

PostgreSQLはMVCC(多版同時実行制御)を採用しているため、たとえインデックスにデータが存在していても、そのデータが現在のトランザクションから見て「可視(Visible)」であるかどうかを確認しなければならない。通常は、インデックスが指し示すヒープ(実テーブル)のページを参照し、Tupleのヘッダー情報を確認する必要がある。

しかし、`Visibility Map` が対象ページに対して「すべてのTupleが全トランザクションから可視である」とマークしていれば、ヒープを参照することなく、インデックス上のデータだけでクエリを完結させられる。これが「Index Only Scan」の正体だ。

もし `INCLUDE` 句を使っていない状態で、インデックスに多くのカラムを詰め込んでいるなら、それはインデックスの深さ(高さ)を無駄に増やし、バッファキャッシュの効率を著しく低下させている可能性がある。`INCLUDE` を活用すれば、検索用のキーは最小限に抑えつつ、必要なデータだけをリーフに忍ばせることができる。

パフォーマンストラブルの勘所:いつ「罠」になるか

ただ、この機能も銀の弾丸ではない。エンジニアとして注意すべきポイントがいくつかある。

1. 書き込みペナルティの増大:
`INCLUDE` したカラムが頻繁に更新される場合、当然ながらインデックスの更新コストは跳ね上がる。`UPDATE` のたびにインデックスのリーフページも書き換わるからだ。読み取り専用に近いテーブルか、更新頻度が低いカラムを選ぶのが鉄則だ。
2. インデックスサイズの肥大化:
当たり前だが、インデックスはタダではない。あまりに多くのカラムを `INCLUDE` しすぎると、`Index Only Scan` の恩恵よりも、インデックスを読み込むためのI/Oコストの方が上回る。`pg_relation_size` でインデックスの肥大化を常にモニタリングする癖をつけてほしい。
3. オプティマイザの気まぐれ:
統計情報が古いと、せっかく `INCLUDE` を定義したのに、プランナが「ヒープアクセスしたほうが速い」と判断して `Bitmap Heap Scan` を選択することがある。`EXPLAIN (ANALYZE, BUFFERS)` を取り、`Heap Fetches` の値が本当にゼロになっているかを確認する習慣は、プロのたしなみだ。

最後に:エンジニアとしての嗅覚

結局のところ、インデックス設計とは「トレードオフの芸術」だ。

`INCLUDE` 句は、テーブルへのヒープアクセスという、データベースエンジンにとって最も高コストな操作を回避するための強力な武器だ。しかし、それを活かすも殺すも、テーブルの更新パターンやキャッシュのヒット率に対する深い洞察にかかっている。

「とりあえずインデックスを貼る」段階を卒業し、「このクエリの実行計画において、どのデータがどこに配置されるのが最も効率的か」を想像できるようになったとき、PostgreSQLはまた一つ、面白い道具になるはずだ。

皆さんのデータベースに、無駄なヒープアクセスが減ることを祈っている。それでは、また。

コメント

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