示例说明
EXPLAIN 只显示预估计划,ANALYZE 才执行并附上每步的真实行数与耗时——预估与实际的偏差本身就是线索:偏差数倍说明统计信息过期,优化器在盲选计划。BUFFERS 再加一层磁盘 IO 真相——shared hit 与 read 的比例揭示缓存命中率,热点数据与冷数据的性能差异由此解释。
技术要点
- ANALYZE 附带每算子的真实行数
- BUFFERS 揭示缓存与磁盘比例
- 预实偏差指向过期统计信息
代码解读
主查询展示联表计划的全貌:扫描方式、联结策略、排序节点层层展开,LIMIT 截断在计划树上可见;第二个查询对比统计刷新前后的行数估算;最后建复合索引并验证查询计划切换为索引扫描——索引是否真被采用只能由计划证实,不能靠直觉。
使用建议
ANALYZE 会真实执行,生产慢查询在从库或限流后跑。行数估算的 n_distinct 直方图可手动调整。语句级计时参数 track_io_timing 需要开启才有精确 IO。
代码示例
-- BUFFERS 选项显示磁盘与缓存命中,真实成本的核心
EXPLAIN (ANALYZE, BUFFERS)
SELECT o.id, c.email
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.status = 'paid'
AND o.created_at >= '2026-10-01'
ORDER BY o.created_at DESC
LIMIT 50;
-- 检查估算偏差:预估行数与实际行数差距大时统计信息过期
EXPLAIN (ANALYZE)
SELECT count(*)
FROM orders
WHERE customer_id = 42;
-- 统计信息刷新后重测
ANALYZE orders;
EXPLAIN (ANALYZE)
SELECT count(*)
FROM orders
WHERE customer_id = 42;
-- 验证新索引被采用:Index Scan / Index Only Scan
CREATE INDEX CONCURRENTLY idx_orders_customer_status
ON orders (customer_id, status);
EXPLAIN (ANALYZE)
SELECT id FROM orders
WHERE customer_id = 42 AND status = 'paid';