はじめに
PostgreSQL はインデックスのタイプが豊富で、適切に使い分けることでクエリパフォーマンスを大幅に改善できます。本記事では、実践的なインデックス設計の指針を解説します。
インデックスタイプの使い分け
B-tree(デフォルト)
等価比較や範囲検索に最適です。ほとんどのケースでこれを使います。
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_orders_created_at ON orders (created_at DESC);
複合インデックス
複数のカラムを組み合わせたクエリには複合インデックスが有効です。カラムの順序が重要です。
-- WHERE tenant_id = ? AND status = ? AND created_at > ?
CREATE INDEX idx_orders_tenant_status_date
ON orders (tenant_id, status, created_at);
GIN(Generalized Inverted Index)
配列や JSONB、全文検索に適しています。
-- JSONB フィールドの検索
CREATE INDEX idx_products_metadata ON products USING GIN (metadata);
-- 配列の検索
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);
部分インデックス
特定の条件に一致する行だけにインデックスを作ることで、サイズを小さく保てます。
-- アクティブなユーザーだけにインデックス
CREATE INDEX idx_active_users ON users (email)
WHERE deleted_at IS NULL;
EXPLAIN ANALYZE を活用する
インデックスが効いているかどうかは、必ず EXPLAIN ANALYZE で確認します。
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE tenant_id = 'abc' AND status = 'pending'
ORDER BY created_at DESC
LIMIT 20;
Seq Scan が表示された場合、インデックスが使われていない可能性があります。
インデックスの落とし穴
- 過剰なインデックス: INSERT/UPDATE のパフォーマンスに影響します
- 低カーディナリティのカラム:
boolean型のカラムにインデックスを貼っても効果は薄いです - 関数を使ったクエリ:
WHERE LOWER(email) = ?では通常のインデックスが効きません
まとめ
インデックスは適切に使えばパフォーマンスを劇的に改善できますが、むやみに追加すると逆効果になります。クエリパターンを分析し、必要なインデックスだけを作ることが重要です。