【実務・中級編】 関数インデックス (Expression Index) – PostgreSQL

「それ、全件スキャンしてない?」PostgreSQLの関数インデックスで検索を劇的に速くする魔法

やあ。最近、コードレビューで「検索が遅い」という相談を受けることが増えたんだけど、その原因の多くが「インデックスを貼っているのに、それが使われていない」という悲しい状況なんだよね。

特に多いのが、`WHERE`句で列に対して関数をかませちゃっているパターン。「これ、インデックス貼ってあるじゃん!」って言いたくなる気持ちもわかるんだけど、DBはそんなに甘くない。

今日は、そんな悩みを一発で解決する「関数インデックス(Expression Index)」という、現場でマジで重宝するテクニックについて話そうと思う。これを知っているだけで、パフォーマンスチューニングの引き出しがグッと増えるはずだよ。

—

なぜ普通のインデックスが効かないのか?

まずは復習から。PostgreSQLで`CREATE INDEX`を作ると、テーブルの「列の値」そのものをB-Tree構造に並べるよね。

でも、こんなクエリを投げたらどうなると思う?

SELECT FROM users WHERE LOWER(email) = ‘hoge@example.com’;

このとき、DBは「`email`列の値」を知っているけど、「`LOWER(email)`の結果」がどうなっているかは知らないんだ。だから、結局テーブルを全件走査(シーケンシャルスキャン)する羽目になる。これが遅延の正体。

関数インデックスの登場

ここで登場するのが、計算結果そのものをインデックスにぶち込む「関数インデックス」だ。

CREATE INDEX idx_users_lower_email ON users (LOWER(email));

こう書くと、PostgreSQLは「`LOWER(email)`の計算結果」をインデックスとして保持してくれる。次に同じクエリを投げたとき、DBはインデックスを辿って一瞬でレコードを見つけてくれるようになるんだ。

—

実務で「これ使える!」と思った3つのユースケース

教科書的な話はここまでにして、現場で僕がよく使うケースをシェアするね。

1. 大文字・小文字を無視した検索

さっきのメールアドレスの例がまさにこれ。ユーザーが入力したメールアドレスの揺れを吸収したいとき、わざわざカラムを小文字で正規化して保存しなくても、関数インデックスさえあれば綺麗なデータ構造を維持したまま爆速検索ができる。

2. JSONB内の特定のキーを検索

最近のプロダクトだと、JSONB型で柔軟にデータを持ちたい場面も多いよね。例えば、`metadata`というJSONBカラムの中に`{ “status”: “active” }`みたいなデータが入っている場合:

— これだとインデックスが効かないことが多い
SELECT FROM orders WHERE metadata->>’status’ = ‘active’;

— こうやってインデックスを貼る!
CREATE INDEX idx_orders_status ON orders ((metadata->>’status’));

※注意:JSONBの演算は複雑だから、`->>`なのか`->`なのか、型キャストが必要かを確認するのがコツだよ。

3. 部分的な日付検索(年・月だけ抽出)

「2023年のデータだけ取りたい」なんて要件、よくあるよね。`created_at`(タイムスタンプ)から年だけを抽出する場合にも有効だ。

CREATE INDEX idx_orders_year ON orders (EXTRACT(YEAR FROM created_at));

—

使う前に知っておいてほしい「注意点」

ただ、便利な反面、気をつけてほしいこともある。

  • 書き込み性能のトレードオフ:

当たり前だけど、インデックスが増えるということは、`INSERT`や`UPDATE`のたびに計算コストがかかるということ。闇雲に何でもインデックスを貼ればいいわけじゃない。

  • クエリとインデックスの完全一致:

`LOWER(email)`でインデックスを作ったなら、クエリ側も必ず`LOWER(email)`と記述しないとダメだ。「`UPPER(email)`」とか「`LOWER(email) || ‘ ‘`」みたいに少しでも式が変わると、DBはインデックスを無視しちゃうから気をつけてね。

  • ストレージ容量:

関数インデックスは実質的に「計算結果という名のカラム」を裏で管理しているようなもの。巨大なテーブルだとそれなりのサイズを食うから、`EXPLAIN ANALYZE`で本当に効いているか確認する癖をつけよう。

—

最後に

「インデックスは貼るもの」じゃなくて、「クエリに合わせて設計するもの」。

この感覚を掴むと、パフォーマンスチューニングがパズルみたいで楽しくなってくるはずだよ。もし「この検索、インデックス貼ってるのに遅いんだよな…」と悩んでいる後輩がいたら、まずはそのクエリの`WHERE`句を覗いてみて。そこに計算式が潜んでいたら、関数インデックスの出番だ。

もし何か詰まったら、いつでも聞きに来てよ。現場からは以上です!

コメント

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