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/O | shared read が多いとディスクI/Oでクエリが大幅遅延 |
実際に起こる障害:統計情報の風化による Seq Scan 固執事故
大量の INSERT / UPDATE / DELETE が発生したテーブルで、ANALYZE コマンドが実行されていない場合、PostgreSQLのオプティマイザは「テーブルが小さい」「インデックスを使うより全件スキャンの方が早い」と誤った判断(rows予測の著しい乖離) を行います。
インデックスが存在するにもかかわらず Seq Scan を選択し続けた結果、大量の shared read(ディスク読み取り)が発生し、DBサーバー全体のI/O帯域(EBS IOPS等)を喰いつくしてシステム全体が停止する障害 が発生します。
改善手順
- 実行計画
EXPLAIN (ANALYZE, BUFFERS)でSeq Scanとshared readの発生箇所を特定する - 見積もり
rowsとactual rowsに大きな乖離がある場合は、まずANALYZE <テーブル名>;を実行して統計情報を最新化する - 検索条件(
WHERE/JOIN) カラムに対して最適な単一/複合インデックスを作成する - 再度
EXPLAINを呼び出し、Index ScanまたはBitmap Index Scanに切り替わったことを確認する
-- 1. 統計情報の最新化(オプティマイザに最新の行数分布を提示)
ANALYZE orders;
-- 2. 複合インデックスの作成
CREATE INDEX idx_orders_user_status ON orders (user_id, status);