PostgreSQL EXPLAIN
EXPLAIN 用来告诉你:PostgreSQL 打算“如何执行”这条 SQL
EXPLAIN SELECT * FROM users WHERE id = 1;
👉 不执行 SQL,只生成 执行计划(Execution Plan)
| 命令 | 是否执行 SQL | 用途 |
|---|---|---|
| EXPLAIN | ❌ 不执行 | 看优化器“打算怎么跑” |
| EXPLAIN ANALYZE | ✅ 会执行 | 看真实执行情况(最准) |
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;
⚠️ 生产环境慎用 ANALYZE(可能是 UPDATE / DELETE)
一个典型 EXPLAIN:
Seq Scan on users (cost=0.00..35.50 rows=5 width=64)
Filter: (age > 30)
核心结构
[执行节点]
└─ 子节点
👉 从内到外读,从上到下执行
(cost=0.00..35.50 rows=5 width=64)
| 字段 | 含义 |
|---|---|
| cost | 执行成本(估算值) |
| rows | 预估返回行数 |
| width | 每行平均字节数 |
| actual time | 真实耗时(ANALYZE) |
| loops | 节点执行次数 |
cost = 启动成本 .. 总成本
- 启动成本:返回第一行前的代价
- 总成本:返回所有行的代价
👉 cost 不是时间,是相对值
Seq Scan on users
含义
- 从头扫到尾
- 大表通常是性能杀手
出现原因
- 无索引
- 条件不走索引
- 返回行太多
Index Scan using users_pkey on users
特点
- 先走索引
- 再回表(heap)
适用
- 返回少量行
Index Only Scan using idx_users_age
特点(性能最好)
- 不回表
- 只读索引
条件
- 查询字段都在索引中
- visibility map 足够干净(VACUUM)
Bitmap Index Scan
Bitmap Heap Scan
适用
- 返回中等数量行
- 多条件索引
👉 常见于:
WHERE a = 1 AND b = 2
Nested Loop
特点
- 小表驱动大表
- 适合:
- 主键 JOIN
- 小结果集
危险
- 大表 × 大表 = 灾难
Hash Join
特点
- 构建 hash 表
- 适合等值 JOIN
ON a.id = b.id
Merge Join
特点
- 双方必须有序
- 常用于:
- 已排序索引
- ORDER BY + JOIN
Index Cond: (id = 1)
Filter: (status = 'active')
| 项 | 含义 |
|---|---|
| Index Cond | 在索引层过滤 |
| Filter | 回表后再过滤(慢) |
👉 Filter 越多,索引越没用
actual time=0.025..0.030 rows=1 loops=1
重点关注
- actual time 是否远大于 cost
- rows 预估 vs 实际是否差很多
- loops 是否异常
估算不准的原因
- 统计信息过旧
- 数据分布不均
解决
ANALYZE table_name;
Seq Scan on big_table
可能原因:
- 函数包裹字段
- 隐式类型转换
- LIKE ‘%xxx%’
rows=10 actual rows=100000
原因:
- 统计信息不准
- 需要 extended statistics
CREATE STATISTICS s1 ON a, b FROM t;
ANALYZE t;
Nested Loop (rows=100000 loops=100000)
解决:
- 加索引
- 改 JOIN 顺序
- 提高 work_mem(允许 Hash Join)
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT ...
| 参数 | 作用 |
|---|---|
| ANALYZE | 真实执行 |
| BUFFERS | 显示缓存命中 |
| VERBOSE | 显示详细字段 |
| TIMING | 显示精确时间 |
BUFFERS 示例
Buffers: shared hit=120 read=10
- hit:命中缓存
- read:磁盘读取(慢)
一句口诀:
先看 Scan -> 再看 Join -> 再看 rows -> 最后看 Buffers
标准流程
- 是否全表扫描?
- 索引是否命中?
- JOIN 方式是否合理?
- rows 预估是否离谱?
- 是否大量磁盘 IO?
A:不是,是相对代价单位。
A:返回行太多,优化器认为顺扫更快。
A:不回表,只读索引。
A:大表等值 JOIN。
A:ANALYZE、VACUUM、extended statistics。
EXPLAIN (ANALYZE, BUFFERS)
SELECT ...