【実務・中級編】 式インデックス(Expression Indexes) – PostgreSQL

「なんでインデックス効かないの?」を卒業しよう。PostgreSQLの「式インデックス」でパフォーマンスを劇的に改善する話

現場でコードを書いていて、こんな経験はありませんか?

「カラムにはちゃんとインデックスを貼ったはずなのに、`EXPLAIN` を叩くと『Seq Scan(全件走査)』になってる……!」

その原因、実は「WHERE句で関数や演算を使っているから」かもしれません。PostgreSQLに限らず、データベースは基本的に「そのままの値」をインデックスとして保存します。だから、`WHERE lower(email) = ‘example@gmail.com’` なんて書くと、データベースは「`lower(email)` の値なんて知らんわ!」とばかりに、全行を計算して確認しにいくわけです。

今日は、そんな悩みを一発で解決する「式インデックス(Expression Indexes)」という武器についてお話しします。これを知っているだけで、クエリチューニングの引き出しがグッと増えますよ。

—

式インデックスって何者?

一言で言えば、「テーブルの列そのものではなく、計算結果や関数適用後の値に対してインデックスを作成する」仕組みです。

通常のインデックスが「辞書のような索引」だとしたら、式インデックスは「計算ドリルを解いた後の解答集をあらかじめ作っておく」ようなイメージですね。一度作ってしまえば、PostgreSQLはクエリのたびに計算し直す必要がなくなるので、爆速で結果を返せるようになります。

具体的な使用シーンを見てみよう

1. よくある「大文字・小文字」問題

ユーザー検索などで、`lower()` 関数を使ってケースインセンシティブな検索をすることは多いですよね。

— よくあるダメな例
SELECT FROM users WHERE lower(username) = ‘tanaka’;

これだとインデックスが無視されます。ここで登場するのが式インデックスです。

— これで解決!
CREATE INDEX idx_users_lower_username ON users (lower(username));

これを作成した瞬間に、さっきのクエリは爆速になります。PostgreSQLはインデックスの中に「変換後の値」を保持してくれるようになるからです。

2. JSONBデータの特定のキーを狙い撃ち

最近のモダンなアプリだと、JSONB型を多用しますよね。でも、JSONの中身を毎回パースするのはコストが高い。

— 注文データのJSONから、特定のIDを検索したいとき
CREATE INDEX idx_orders_customer_id ON orders ((data->>’customer_id’));

※JSONBのパスに対するインデックスには `( )` が二重に必要な点に注意してくださいね。これ、よく忘れてハマるポイントです。

注意点:銀の弾丸ではない

「じゃあ、何でもかんでも式インデックスを貼ればいいじゃん!」……と言いたいところですが、そこは落ち着いて。いくつか注意点があります。

  • 書き込みコストが増える: インデックスが増えるということは、`INSERT` や `UPDATE` のたびにその計算処理が走るということです。読み取りは速くなりますが、書き込みのオーバーヘッドは確実に増えます。
  • クエリの書き方と完全に一致させること: インデックスを作成した式と、クエリで書く式が1文字でも違えば(スペースの有無や関数の引数など)、インデックスは使われません。
  • `lower(col)` でインデックスを作ったなら、クエリも必ず `lower(col)` にする必要があります。

先輩からのアドバイス:まずは「EXPLAIN ANALYZE」を信じろ

私が新人の頃に教わって一番良かったのは、「自分の直感を信じる前に、実行計画を信じろ」という教訓です。

`EXPLAIN (ANALYZE, BUFFERS) SELECT …` を叩いて、実行計画を見てみてください。もし「Seq Scan」や「Filter」という文字が見えたら、それは「インデックスがサボっている証拠」です。

式インデックスは、まさにそんな現場の「困った」を解決する特効薬です。ただし、使いすぎはデータベースの肥満化を招くので、本当にパフォーマンスがボトルネックになっている箇所にだけ、ピンポイントで投入するのがプロのやり方です。

皆さんの現場のクエリも、一度 `EXPLAIN` で診断してみませんか?意外なところで「もっと速くできる!」という発見があるかもしれませんよ。

それでは、また次回の記事でお会いしましょう!Happy Query Tuning!

コメント

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