「またクエリが遅いって言われてるんだよ……」
データベースのパフォーマンスチューニングをしていると、そんな泣き言を開発メンバーから聞くことは一度や二度じゃありませんよね。特に「WHERE句で検索しているのにインデックスが効いていない」というケースは、現場あるあるの筆頭です。
「いやいや、ちゃんとインデックスは貼ってあるよ!」なんて言いたくなる気持ちもわかります。でも、PostgreSQLがそのインデックスをスルーしているのには、ちゃんと理由があるんです。
今日はそんな「インデックスが効かない」という地獄から抜け出すための最強の武器、「関数ベースのインデックス(Function-based Indexes)」について、現場の知恵を共有しようと思います。
—
なぜ、インデックスが無視されるのか?
まずは、こんなテーブルを想像してください。ユーザー管理のテーブルで、メールアドレスを保存しているとします。
SELECT FROM users WHERE LOWER(email) = ‘yamada@example.com’;
このクエリ、もし`email`カラムに普通のB-treeインデックスが貼ってあったとしても、PostgreSQLはそれを使ってくれません。なぜなら、データベースは「`email`カラムの値」をインデックス化しているだけで、「`LOWER(email)`という変換後の値」までは計算して保存していないからです。
結果、DBエンジンは泣く泣くテーブル全体をスキャンする「シーケンシャルスキャン」を選択します。数万件ならまだしも、数百万件あったら……まあ、お察しの通りですね。
—
救世主「関数ベースのインデックス」の出番
ここで登場するのが、特定の式や関数の結果をインデックス化する手法です。やり方は驚くほどシンプル。インデックスを作成する際に、カラム名ではなく「式」をそのまま書くだけです。
CREATE INDEX idx_users_lower_email ON users (LOWER(email));
これだけで、PostgreSQLは`LOWER(email)`の結果をあらかじめ計算してインデックスツリーに格納してくれます。次にクエリが投げられたとき、オプティマイザは「あ、これインデックスが使えるぞ!」と即座に判断して、爆速で結果を返してくれるようになります。
—
現場でよく使う「実践的な活用例」
実務でこのテクニックをどう使うか、いくつか「現場で使えるパターン」を紹介しますね。
1. JSONBの検索を高速化する
PostgreSQLのJSONBは強力ですが、中身を検索するクエリは重くなりがちです。例えば、JSON内の特定のフィールドを条件にするなら、関数ベースのインデックスが必須レベルです。
— JSONB内の ‘name’ フィールドに対してインデックスを作成
CREATE INDEX idx_users_json_name ON users ((data->>’name’));
こうしておけば、`WHERE data->>’name’ = ‘Tanaka’` というクエリが劇的に速くなります。
2. 日付の「年」や「月」で集計する
「特定の月のデータを頻繁に抽出する」といったダッシュボード的な機能では、日付関数との組み合わせが効きます。
CREATE INDEX idx_orders_created_month ON orders (DATE_TRUNC(‘month’, created_at));
これをしておかないと、`WHERE DATE_TRUNC(‘month’, created_at) = ‘2023-10-01’` なんていうクエリが毎回フルスキャンすることになります。
—
使う前に知っておいてほしい「注意点」
これだけ便利な機能ですが、先輩として一つだけ釘を刺しておきたいことがあります。
- 書き込み負荷(オーバーヘッド)に注意
インデックスはあくまで「読み取り」を速くするためのものです。関数ベースのインデックスを貼ると、そのテーブルに`INSERT`や`UPDATE`が発生するたびに、データベースはその「関数の計算」を裏で行わなければなりません。あまりに大量にインデックスを作りすぎると、今度は書き込み速度が低下します。「本当にこのインデックスは必要か?」というバランス感覚を忘れないでください。
- クエリの書き方と完全に一致させること
インデックスに `LOWER(email)` を指定したなら、クエリも必ず `LOWER(email)` にしないと意味がありません。例えば `UPPER(email)` と書いてしまえば、インデックスは使われません。クエリの書き方とインデックスの定義はセットで管理しましょう。
—
最後に:データベースは対話だ
インデックス設計は、データベースとの対話です。「どんな検索が頻繁に行われるのか?」「このテーブルは更新頻度が高いのか?」という問いを繰り返すことで、最適な形が見えてきます。
関数ベースのインデックスは、そんな対話の中で「どうすればこの検索を速くできるか」という悩みを解決してくれる、非常に強力なカードです。
「なんとなく遅いから」と諦める前に、ぜひ実行計画(`EXPLAIN ANALYZE`)を見てみてください。そこに、今回紹介したテクニックが刺さる余地が残されているはずですよ。
それでは、良いパフォーマンスライフを!
コメント