コンテンツにスキップ

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 レスポンスが遅い原因を調査するとき
  • 「まず計測してから最適化」の原則を実践する場面
  • コードを書き直す前に、クエリ最適化で解決できるか確認するとき
  • コードレビューでパフォーマンスについて議論するとき