APIレスポンスを1.2s→80msに改善:EXPLAIN ANALYZEで20分でボトルネックを特定する方法
概要¶
同じコードベース・新ツールなし・書き直しなしで、API レスポンスタイムを 1.2s から 80ms に改善した事例。GET /orders エンドポイントを EXPLAIN ANALYZE で調査し、20分で主要な問題を特定。
詳細¶
発見した問題¶
1. Over-Fetching(過剰取得)¶
-- 悪い例
SELECT * FROM orders WHERE user_id = ?
-- → 47カラム全てを取得、UIが使うのは数カラムだけ
-- 改善後
SELECT id, status, total, created_at FROM orders WHERE user_id = ?
-- → 必要なカラムのみ取得
2. N+1 クエリ問題¶
-- 悪い例(N+1): ループで1件ずつ取得
SELECT * FROM orders WHERE user_id = ? -- 1クエリ
SELECT * FROM items WHERE order_id = 1 -- N クエリ
SELECT * FROM items WHERE order_id = 2
...
-- 改善後(JOIN で一括取得)
SELECT o.*, i.*
FROM orders o
LEFT JOIN items i ON i.order_id = o.id
WHERE o.user_id = ?
3. インデックス不足¶
-- EXPLAIN ANALYZE で Seq Scan が出たら要注意
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = ?;
-- Seq Scan on orders → インデックスなし
-- 改善
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- → Index Scan に変わり劇的に高速化
EXPLAIN ANALYZE の使い方¶
EXPLAIN ANALYZE
SELECT o.id, o.status, o.total
FROM orders o
WHERE o.user_id = $1
ORDER BY o.created_at DESC
LIMIT 20;
-- 注目すべきポイント:
-- "Seq Scan" → インデックス不使用、テーブル全件スキャン
-- "actual time" → 実際の実行時間
-- "rows" → 返却行数の推定値 vs 実値
-- "cost" → 相対コスト(小さいほど良い)
なぜ重要か / いつ使うか¶
- API レスポンスが遅い原因を調査するとき
- 「まず計測してから最適化」の原則を実践する場面
- コードを書き直す前に、クエリ最適化で解決できるか確認するとき
- コードレビューでパフォーマンスについて議論するとき