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

「またクエリが遅いって言われてるんだよ……」

データベースのパフォーマンスチューニングをしていると、そんな泣き言を開発メンバーから聞くことは一度や二度じゃありませんよね。特に「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`)を見てみてください。そこに、今回紹介したテクニックが刺さる余地が残されているはずですよ。

それでは、良いパフォーマンスライフを!

コメント

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