PostgreSQL EXPLAIN ANALYZEでSeq ScanからIndex Scanへ導く読み方

PostgreSQL Database Performance SQL
結論

EXPLAIN (ANALYZE, BUFFERS) を実行し、shared read 数値と見積もり行数(rows)の誤差を分析します。

-- PostgreSQL公式仕様:実行計画とキャッシュ状態の同時取得
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders 
WHERE user_id = 42 AND status = 'shipped';

-- 出力解析例:
-- Seq Scan on orders  (cost=0.00..1845.00 rows=12 width=64) (actual time=0.045..12.350 rows=12 loops=1)
--   Filter: (user_id = 42 AND status = 'shipped'::text)
--   Buffers: shared hit=42 shared read=180

PostgreSQL公式仕様:実行計画の主要インジケータ

PostgreSQL公式ドキュメント(postgresql.org/docs/current/using-explain.html)における EXPLAIN (ANALYZE, BUFFERS) の各項目定義です。

項目意味注目点
Seq Scan全件テーブルスキャン該当テーブル全行を1行ずつ読み出している
cost=0.00..1845.00推定コスト(開始..完了)オプティマイザが計算したディスクアクセス負荷
rows=12 vs actual rows=12行数予測と実際の取得行数乖離が大きい場合、統計情報(pg_statistic)が古い
shared hit / shared readキャッシュ / ディスクI/Oshared read が多いとディスクI/Oでクエリが大幅遅延

実際に起こる障害:統計情報の風化による Seq Scan 固執事故

大量の INSERT / UPDATE / DELETE が発生したテーブルで、ANALYZE コマンドが実行されていない場合、PostgreSQLのオプティマイザは「テーブルが小さい」「インデックスを使うより全件スキャンの方が早い」と誤った判断(rows予測の著しい乖離) を行います。

インデックスが存在するにもかかわらず Seq Scan を選択し続けた結果、大量の shared read(ディスク読み取り)が発生し、DBサーバー全体のI/O帯域(EBS IOPS等)を喰いつくしてシステム全体が停止する障害 が発生します。


改善手順

  1. 実行計画 EXPLAIN (ANALYZE, BUFFERS)Seq Scanshared read の発生箇所を特定する
  2. 見積もり rowsactual rows に大きな乖離がある場合は、まず ANALYZE <テーブル名>; を実行して統計情報を最新化する
  3. 検索条件(WHERE / JOIN) カラムに対して最適な単一/複合インデックスを作成する
  4. 再度 EXPLAIN を呼び出し、Index Scan または Bitmap Index Scan に切り替わったことを確認する
-- 1. 統計情報の最新化(オプティマイザに最新の行数分布を提示)
ANALYZE orders;

-- 2. 複合インデックスの作成
CREATE INDEX idx_orders_user_status ON orders (user_id, status);