EXPLAIN 和 EXPLAIN ANALYZE
PostgreSQL/SQL 调优 - EXPLAIN
和 EXPLAIN ANALYZE
1️⃣ 基础概念
命令 | 作用 | 是否执行 SQL | 输出特点 |
---|---|---|---|
EXPLAIN | 显示查询执行计划 | ❌ 不执行 | 只显示 planner 估算的执行步骤、行数、成本 |
EXPLAIN ANALYZE | 显示查询执行计划 + 实际执行统计 | ✅ 执行 SQL | 显示 planner 估算和实际执行差异,包括耗时和实际行数 |
2️⃣ 输出关键字段解释
以 EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123;
为例:
Index Scan using idx_orders_customer_id on orders(cost=0.29..8.30 rows=1 width=244)(actual time=0.012..0.014 rows=5 loops=1)
字段 | 含义 |
---|---|
Index Scan using idx_orders_customer_id | 实际使用了哪种扫描方式(这里是索引扫描) |
cost=0.29..8.30 | 估算执行成本,左边是起始成本,右边是总成本 |
rows=1 | planner 估算的行数 |
width=244 | 每行字节大小估算 |
actual time=0.012..0.014 | 实际执行时间,左边是开始时间,右边是结束时间(毫秒) |
rows=5 | 实际返回行数 |
loops=1 | 该步骤执行的次数(通常 join 会多次执行子节点) |
3️⃣ 常用执行类型
Seq Scan(顺序扫描)
遍历全表,可能慢,大表要注意。Index Scan(索引扫描)
使用索引查询,快速返回。Bitmap Index Scan + Bitmap Heap Scan
使用索引先找到匹配行,再去表中读取数据。适合范围查询。Nested Loop / Hash Join / Merge Join
表连接方式:Nested Loop:小表驱动大表,循环嵌套。
Hash Join:对一个表构建 hash table,然后扫描另一个表。
Merge Join:两表排序后合并,适合已排序或有索引列。
4️⃣ 使用技巧
4.1 对比估算 vs 实际
EXPLAIN ANALYZE
能显示 planner 的估算(cost、rows)和实际值(actual rows、actual time)。如果差异大:
可能统计信息不准确,需要
ANALYZE table_name
。可能索引缺失或选择了错误的 join 策略。
4.2 仅查看执行计划,不执行
EXPLAIN SELECT * FROM orders WHERE customer_id = 123;
可快速查看 planner 打算怎么走索引。
大数据量表或者写操作不希望实际执行时使用。
4.3 包含详细计划
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT TEXT)
SELECT * FROM orders WHERE customer_id = 123;
BUFFERS
:显示实际使用的缓冲页(shared_buffers / disk)。VERBOSE
:显示更多节点信息(如 filter 条件、表别名)。FORMAT JSON
/FORMAT YAML
:便于程序解析。
4.4 慎用在写操作
EXPLAIN ANALYZE DELETE FROM orders WHERE id = 1;
真正执行 SQL,会修改表数据。
可用事务回滚保护:
BEGIN;
EXPLAIN ANALYZE DELETE FROM orders WHERE id = 1;
ROLLBACK;
4.5 结合统计信息
对估算行数不准的表:
ANALYZE orders;
更新表和索引的统计信息,让 planner 更精准。
5️⃣ 排查慢查询的实战步骤
查看执行计划
EXPLAIN SELECT * FROM orders WHERE customer_id = 123;
检查是否使用了索引。
检查 join 方式和预计行数。
执行真实计划
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123;
对比
rows
vsactual rows
,查看误差。对比
cost
vsactual time
,找出耗时步骤。
分析扫描方式
顺序扫描大表 → 考虑索引。
Nested Loop 循环大表 → 考虑改 Hash Join 或优化条件。
查看 I/O 消耗
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 123;
查看是否大量访问磁盘页,优化索引或数据布局。
调优索引或 SQL
建复合索引或覆盖索引。
修改查询顺序或条件。
用
LIMIT
或分页减少一次返回数据量。
6️⃣ 总结
特性 | EXPLAIN | EXPLAIN ANALYZE |
---|---|---|
执行 SQL | ❌ | ✅ |
显示执行计划 | ✅ | ✅ |
显示实际耗时 | ❌ | ✅ |
显示实际行数 | ❌ | ✅ |
慎用大写/写操作 | ✅ | ⚠️(会真正修改) |
用途 | 查看 planner 计划 | 排查慢查询、调优 SQL |