← 返回题目列表

PostgreSQL EXPLAIN 和 EXPLAIN ANALYZE 怎么看?

高频 中等 第 16 / 31 题 更新于 2026/07/28
PostgreSQLEXPLAIN执行计划SQL优化

简化版

EXPLAIN 用来看 PostgreSQL 计划如何执行 SQL,EXPLAIN ANALYZE 会实际执行 SQL 并输出真实耗时和行数。看执行计划时重点关注扫描方式、JOIN 方式、估算行数和真实行数差距、排序和聚合成本、循环次数,以及是否出现不该有的全表扫描。

详细版

常见执行计划信息包括:

  • Seq Scan:顺序扫描,可能是全表扫描;
  • Index Scan / Index Only Scan:使用索引扫描;
  • Bitmap Index Scan / Bitmap Heap Scan:先用索引定位,再批量回表;
  • Nested Loop:嵌套循环连接,小结果集常见;
  • Hash Join:哈希连接,大量等值连接常见;
  • Sort:排序节点,可能消耗内存或落盘;
  • cost:优化器估算成本;
  • rows:估算行数;
  • actual time:真实执行耗时;
  • loops:节点执行次数。

优化时不能只看有没有走索引,还要看估算是否准确、返回行数是否合理、索引选择是否真的降低成本。

完整版教学

一、EXPLAIN 和 EXPLAIN ANALYZE 的区别

EXPLAIN 只展示计划,不真正执行语句:

EXPLAIN SELECT * FROM orders WHERE user_id = 100;

EXPLAIN ANALYZE 会实际执行,并输出真实执行信息:

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 100;

这意味着对 INSERTUPDATEDELETE 使用 EXPLAIN ANALYZE 时要格外小心,因为语句会真的执行。必要时放在事务中测试并回滚。

二、先看扫描方式

扫描方式通常能快速暴露问题:

  • Seq Scan:顺序扫描整张表或大范围数据;
  • Index Scan:通过索引定位,再访问表;
  • Index Only Scan:尽量只从索引返回数据;
  • Bitmap Index Scan:用索引找出匹配位置;
  • Bitmap Heap Scan:按位图回表读取数据页。

看到 Seq Scan 不一定就是错。如果表很小,或者条件命中大量数据,顺序扫描可能比索引更快。真正要判断的是:它和业务预期是否一致。

三、重点比较估算行数和真实行数

执行计划里常见:

rows=100
actual rows=50000

如果估算行数和真实行数差距很大,优化器可能会选错 JOIN 顺序、扫描方式或排序策略。

常见原因包括:

  • 统计信息过旧;
  • 数据分布倾斜;
  • 多列条件存在相关性;
  • 表刚经历大量写入或删除;
  • 表达式、JSONB 字段统计不足。

这时不仅要加索引,也可能需要 ANALYZE、调整统计目标或改写 SQL。

四、看 JOIN 方式是否匹配数据规模

PostgreSQL 常见 JOIN 方式包括:

  • Nested Loop:外层结果很小、内层有索引时效果好;
  • Hash Join:等值连接、大数据集常见;
  • Merge Join:两侧已排序或可高效排序时有优势。

如果外层返回很多行,内层又反复扫描大表,Nested Loop 可能变成性能灾难。此时要检查连接条件、索引、过滤条件下推,以及统计信息是否准确。

五、看排序、聚合和临时开销

ORDER BYGROUP BYDISTINCT、窗口函数都可能引入排序或聚合节点。排序数据量大时,可能消耗大量内存,甚至落盘。

优化方向包括:

  • 建立匹配排序条件的索引;
  • 缩小排序前的数据集;
  • 避免无意义的大分页;
  • 调整查询结构,让过滤尽早发生;
  • 必要时关注 work_mem

不要只把所有问题归结为“没走索引”,复杂 SQL 的瓶颈经常在排序、聚合和 JOIN。

六、常见误区与追问

字段/节点重点看什么典型判断
actual rows vs rows估算是否准确差 100 倍常提示统计信息或数据倾斜问题
loops节点执行次数内层扫描 loops 很大要警惕 Nested Loop
Sort Method排序是否落盘external merge 通常说明内存不足或数据太大

易错点:EXPLAIN ANALYZE 会真实执行 SQL。分析写语句时要放在测试环境,或显式开启事务后回滚,别把“看计划”变成“线上改数据”。

比如计划显示 rows=100,但 actual rows=50000,估算差了 500 倍。优化器原本以为外层只有 100 行,选择 Nested Loop;真实执行时外层 50000 行,每行再访问内表一次,就可能把一个毫秒级查询变成秒级查询。这个时候只加索引未必够,还要考虑 ANALYZE、提高统计目标、处理多列相关性或改写 SQL。

  • 误区:看到 Seq Scan 就一定要加索引。 小表或命中大比例数据时顺序扫描可能更快,要结合实际行数、成本和耗时判断。
  • 误区:EXPLAIN 的 cost 就是真实毫秒数。 cost 是优化器内部估算成本,不等于时间;真实耗时要看 EXPLAIN ANALYZEactual time
  • 误区:走了索引就一定快。 如果回表很多、过滤很弱、排序和 JOIN 很重,索引扫描也可能慢。
  • 追问:估算行数和真实行数差很多怎么办? 先更新统计信息,再看数据倾斜、多列相关性、表达式统计、JSONB 字段统计和 SQL 写法。
  • 追问:Nested Loop 什么时候危险? 外层行数大、内层没有高效索引或 loops 很高时危险;小结果集配内层索引时反而可能很好。
  • 追问:如何判断排序瓶颈? 看 Sort 节点的输入行数、排序方法、是否落盘,以及是否能用匹配索引或先过滤减少排序数据。

七、加强记忆

看 PostgreSQL 执行计划的顺序是:先区分 EXPLAIN 是否真实执行,再看扫描方式、行数估算、JOIN 策略、排序聚合和循环次数。优化不是看到全表扫描就立刻加索引,而是判断计划和数据规模是否匹配。