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

「とりあえずインデックス」で済ませてない?PostgreSQLの「関数ベースインデックス」でクエリを爆速にする話

現場でエンジニアをやっていると、たまにこんなクエリに出くわすよね。

SELECT FROM users WHERE lower(email) = ‘test@example.com’;

で、これに対して「よし、`email`列にインデックスを貼ろう!」といって `CREATE INDEX ON users(email);` を実行する。……でも、パフォーマンスは一向に改善しない。なぜか?

答えは簡単。PostgreSQLのインデックスは「列の値そのもの」を保存しているからです。`lower(email)` という「計算結果」に対して検索をかけているのに、インデックスが持っているのは加工前の値。これじゃあ、データベースは結局フルスキャン(全件検索)するしかないんだ。

今日は、そんな「惜しい」インデックス設計を卒業して、PostgreSQLの隠れた(でも最強の)武器である「関数ベースインデックス(Expression Index)」について深掘りしようと思う。

—

関数ベースインデックスとは何か?

一言で言えば、「計算結果をあらかじめ計算してインデックスに保存しておく」仕組み。

通常、インデックスは `(列の値, ポインタ)` という形で保存されるけど、関数ベースインデックスなら `(関数の計算結果, ポインタ)` という形で保存できる。これを使えば、クエリ実行時に毎回 `lower()` や `jsonb` のパス抽出を走らせる必要がなくなるんだ。

実践例1:大文字・小文字を無視した検索

さっきの `lower()` の例で見てみよう。

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

これを作るだけで、PostgreSQLは賢いから `WHERE lower(email) = ‘…’` というクエリを見ると、自動的にこのインデックスを使ってくれるようになる。

ここでのポイントは、「クエリ側も同じ関数を使わないといけない」ということ。もしクエリを `WHERE email = ‘…’` と書いてしまったら、このインデックスは無視される。インデックス設計とクエリはセットで考える、これが鉄則だね。

実践例2:JSONBの深い階層を叩くとき

最近はPostgreSQLをドキュメントストアとして使うことも増えたよね。例えば、`metadata` という `jsonb` カラムの中に `{“user_id”: 123}` みたいに情報が入っている場合。

— 毎回こんな検索をしていない?
SELECT FROM logs WHERE metadata->>’user_id’ = ‘123’;

これも `metadata` 全体にインデックスを貼っても、あまり効果的じゃないことが多い。こんな時はこうする。

CREATE INDEX idx_logs_user_id ON logs ((metadata->>’user_id’));

カッコが二重になっているのは、PostgreSQLの構文上のルール。これだけで、数百万件あるログの中から特定のIDを瞬時に引けるようになる。JSONBをバリバリ使う現場なら、このテクニックを知っているだけで神扱いされるはずだぜ。

—

注意点:使いすぎは「諸刃の剣」

ここまで読んだ君なら「じゃあ全部関数ベースインデックスにすればいいじゃん!」と思うかもしれない。でも、ちょっと待って。

1. 書き込みコストは増える: インデックスが増えれば、`INSERT` や `UPDATE` のたびに計算が走る。当然、書き込み処理は重くなる。
2. ディスク容量: 計算結果を保存する場所が必要だから、ストレージを消費する。
3. 計算負荷: あまりにも複雑な関数をインデックスにすると、インデックス自体の更新がボトルネックになる。

基本的には、「頻繁に検索されるけど、その計算処理が重いもの」に絞って使うのが、ベテランのやり方だよ。

最後に:EXPLAIN ANALYZE を信じろ

最後のアドバイス。インデックスを作ったら、必ず `EXPLAIN ANALYZE` を叩いて確認してくれ。

EXPLAIN ANALYZE SELECT FROM users WHERE lower(email) = ‘…’;

ここに `Index Scan` と表示されていれば、君の勝ちだ。もし `Seq Scan`(全件検索)のままなら、インデックスが正しく貼れていないか、クエリの書き方がインデックスの定義と微妙にズレている証拠だ。

データベースは、エンジニアの「意図」をどれだけコードに反映できるか、その腕の見せ所だ。インデックスは単なる高速化の道具じゃなくて、DBに対する「こうやって検索してくれ!」という君からのメッセージなんだよ。

さあ、今のプロジェクトの重いクエリ、一つずつ紐解いていこうか。また何か詰まったら相談してくれよな。

コメント

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