「とりあえず全カラムにインデックス」は卒業しよう。PostgreSQLの「部分インデックス」でスマートな最適化を。
こんにちは。最近、若手エンジニアから「データベースのクエリが重いのでインデックスを増やしたんですが、なぜか書き込みまで遅くなってしまいました……」という相談をよく受けます。
あるあるですよね。インデックスは魔法の杖じゃありません。増やせば増やすほどストレージは食うし、更新(INSERT/UPDATE)のたびにインデックスのメンテナンスコストが発生する。
そんな悩みを抱えるあなたに、ぜひ覚えて帰ってほしいのが「部分インデックス(Partial Index)」という武器です。これを知っているだけで、データベースのパフォーマンスチューニングの引き出しがグッと増えますよ。
—
部分インデックスって何?
一言で言えば、「テーブル全体ではなく、特定の条件を満たす行だけに作るインデックス」のことです。
通常のインデックスは、テーブルの全行に対して作られますよね。でも、実務で考えてみてください。本当にそのカラムの「全行」に対して検索をかけますか?
例えば、「未処理の注文データだけを頻繁に検索する」といったケース。この場合、処理済みの膨大なデータをインデックスに含める必要なんてないんです。
具体的な使用例:こんなところで輝く!
例えば、ECサイトの `orders` テーブルを想像してください。
CREATE TABLE orders (
id serial PRIMARY KEY,
status text, — ‘pending’, ‘shipped’, ‘delivered’ など
customer_id int,
created_at timestamp
);
運用が長くなると、このテーブルには何百万件ものデータが溜まります。ここで、管理画面から「未処理(pending)の注文だけを一覧表示したい」というクエリが頻繁に投げられるとします。
よくある「微妙な」インデックス
CREATE INDEX idx_orders_status ON orders(status);
これだと、`shipped`や`delivered`といった、もう検索対象にならないはずの大量のデータまでインデックスに入ってしまいます。インデックスサイズは肥大化し、メモリ効率も悪くなる。これ、非常にもったいないんです。
「部分インデックス」でスマートに解決
ここで部分インデックスの出番です。
CREATE INDEX idx_orders_pending ON orders(created_at)
WHERE status = ‘pending’;
これだけで、`status`が`pending`の行だけを抽出してインデックスを作成してくれます。
- インデックスサイズが劇的に小さい: 本当に必要なデータだけなので、インデックスがメモリに乗りやすくなります。
- 検索が爆速: 無関係な行を読み込む必要がありません。
- 更新コストの削減: `pending`以外の行が更新されても、このインデックスは無視されるので、余計な書き込み負荷がかかりません。
—
現場で役立つ「使いこなしのコツ」
部分インデックスは強力ですが、魔法ではないので以下の点に気をつけてください。
1. クエリのWHERE句と一致させる
部分インデックスが効くのは、クエリ側のWHERE句がインデックスのWHERE条件を含んでいる時だけです。
— このクエリなら部分インデックスが使われる
SELECT FROM orders WHERE status = ‘pending’ ORDER BY created_at;
— このクエリだと、残念ながらインデックスは無視されます
SELECT FROM orders WHERE status = ‘shipped’;
2. 「NULL」の除外にも使える
案外忘れがちですが、NULL値を除外するインデックスも便利です。
— 削除フラグが立っていないものだけインデックスする
CREATE INDEX idx_active_users ON users(email)
WHERE deleted_at IS NULL;
「退会済みユーザーは検索しないよね」というケースなら、これだけでインデックスのサイズを数割カットできることも珍しくありません。
—
最後に:データベースは「引き算」が大事
初心者の頃は「何かあったらインデックスを追加する」という足し算の思考になりがちです。でも、シニアなエンジニアは「本当に必要なインデックスはどれか?」という引き算の視点を持っています。
ストレージの節約、メモリ効率の向上、そして更新速度の維持。これらすべてを両立できる部分インデックスは、まさに「仕事ができるエンジニア」のための機能です。
あなたの担当しているDBにも、実は「全行インデックス」のままで放置されている場所があるかもしれません。ぜひ一度、`pg_stat_user_indexes`などを眺めながら、不要なインデックスを削り、必要な部分にだけ部分インデックスを貼る、そんな整理整頓から始めてみてください。
それでは、また次回の記事でお会いしましょう!Happy Querying!
コメント