Postgres: EXPLAIN (ANALYZE, BUFFERS) で「なぜ遅いか」を見る
EXPLAIN ANALYZE は実測の時間と行数を出す。BUFFERS を足すとページをどこから読んだかが分かり、遅さがディスク I/O なのか CPU なのかを切り分けられる。
いつ使うか
遅いクエリを調べるとき、プランの形(Seq Scan か Index Scan か)だけでなく、「推定と実際の行数のずれ」と「読んだページ数」まで確かめたいとき。
やり方
sql
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;見るところ:
Buffers: shared hit=... read=...:hitは shared_buffers(Postgres のキャッシュ)にあったページ、readは OS キャッシュかディスクから読んだページ。readが大きい遅いクエリは I/O の問題なので、インデックスで読む量を減らすか、Postgres: INCLUDE 付きのカバリングインデックスで Index Only Scan にする でテーブル本体を読まないようにする。rows=(推定)とactual ... rows=(実際)のずれ: 100 倍などと大きくずれていたら統計情報が古い可能性が高い。ANALYZE テーブル名;で統計を取り直してから見直す。推定が外れるとプランナーが不向きな結合方法やスキャンを選ぶ。Sort Method: external merge Disk:が出たら work_mem 不足(Postgres: 「Sort Method: external merge Disk」は work_mem を全体ではなくクエリ単位で上げる を参照)。
注意点
EXPLAIN ANALYZEはクエリを本当に実行する。UPDATE / DELETE / INSERT に付けるとデータが変わるので、トランザクションで包んで戻す:sqlBEGIN; EXPLAIN (ANALYZE, BUFFERS) DELETE FROM sessions WHERE expires_at < now(); ROLLBACK;- 1 回目はキャッシュが冷えていて
readが多く出やすい。2 回目以降と比べて判断する。 - Postgres 18 以降は
EXPLAIN ANALYZEで BUFFERS が既定で付く。それより前の版では明示が必要。 - 本番で重いクエリを ANALYZE 付きで流すと、その分の負荷がかかる。
出典
- @amitsinha__07 · 2026-11-08
- @krishcdbry 2026-11-09(推定と実際のずれ・DML の注意): @krishcdbry · 2026-11-09