一、执行计划的解释

(一) EXPLAIN 参数解释

PostgreSQL中EXPLAIN命令的语法格式如下:

EXPLAIN [ ( option [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement

该命令的可选项“options”如下:

ANALYZE [ boolean ]
VERBOSE [ boolean ]
COSTS [ boolean ]
BUFFERS [ boolean ]
FORMAT { TEXT | XML | JSON | YAML }
一、ANALYZE选项

ANALYZE选项通过实际执行SQL来获得SQL命令的实际执行计划。
ANALYZE选项查看到的执行计划因为真正被执行过, 所以可以看到执行计划每一步耗费了多长时间, 以及它实际返回的行数。

加上ANALYZE选项后是真正执行实际的SQL命令, 如果SQL语句是一个插入、删除、 更新或CREATE TABLE AS语句(这些语句会修改数据库) , 为了不影响实际数据, 可以把EXPLAIN ANALYZE放到一个事务中, 执行完后即回滚事务, 命令如下:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;
二、VERBOSE选项

VERBOSE选项显示计划的附加信息, 如计划树中每个节点输出的各个列, 如果触发器被触发, 还会输出触发器的名称。该选项的值默认为“FALSE”。

三、COSTS选项

COSTS选项显示每个计划节点的启动成本和总成本, 以及估计行数和每行宽度。
该选项的值默认为“TRUE”。

四、BUFFERS选项

BUFFERS选项显示缓冲区使用的信息。
该参数只能与ANALYZE参数一起使用。
显示的缓冲区信息包括共享块读和写的块数、 本地块读和写的块数, 以及临时块读和写的块数。
共享块、 本地块和临时块分别包含表和索引、 临时表和临时索引, 以及在排序和物化计划中使用的磁盘块。
上层节点显示出来的块数包括所有其子节点使用的块数。
该选项的值默认为“FALSE”。

五、FORMAT选项

FORMAT选项指定输出格式, 输出格式可以是TEXT、 XML、 JSON或者YAML。
非文本输出包含与文本输出格式相同的信息, 但其他程序更易于解析。
该参数默认为“TEXT”。

(二) EXPLAIN输出结果解释

相信大部分人第一次学习执行计划都是看不懂图中信息,不急我们这一章就是带领同学们读懂执行计划。

我们可以看到当前操作节点后面都有一个括号,这代表着所在行的操作成本。

  • 每个 (cost=...) 都归属它所在行的操作(GroupAggregate/Sort/Seq Scan 等)。

  • 是优化器用来选择 “最优执行计划” 的依据(会对比不同路径的 cost,选总代价最低的 )。

  • actual time(实际执行时间)不同,cost 是理论估算,actual time 是真实耗时,可结合两者分析查询效率(比如成本估算是否准确、实际执行是否有额外开销)。

如果 cost 和 actual 差距有点大,这时候我们要手动收集统计信息了。

后面的内容“(cost=3055.45..574290.68 rows=180001 width=370)”可以分为以下3个部分:

  • cost=3055.45..574290.68: “cost=”后面有两个数字, 中间由“…”分隔, 第一个数字“3055.45”表示启动的成本, 也就是说, 返回第一行需要多少cost值; 第二个数字表示返回所有数据的成本, 关于成本“cost”后面会解释。

  • rows=10000: 表示会返回10000行。

  • width=36: 表示每行平均宽度为36字节。

成本“cost”用于描述SQL命令的执行代价, 默认情况下, 不同操作的cost值如下:

  • 顺序扫描一个数据块, cost值定为“1”。

  • 随机扫描一个数据块, cost值定为“4”。

  • 处理一个数据行的CPU代价, cost值定为“0.01”。

  • 处理一个索引行的CPU代价, cost值定为“0.005”。

  • 每个操作符的CPU代价为“0.0025”。

根据上面的操作类型, PostgreSQL可以智能地计算出一个SQL命令的执行代价, 虽然计算结果不是很精确, 但大多数情况下够用了。

1. 1. actual time=0.016..0.073

  • 含义:该操作节点从开始到结束的 实际耗时(单位:毫秒)。

  • 两个数值

  • 0.016:操作的 启动时间(Start-up Time),即优化器开始执行该操作的时间点。

  • 0.073:操作的 总耗时(Total Time),即该操作完成的时间点。

  • 差值意义0.073 - 0.016 = 0.057ms 是该操作 真正处理数据的时间(不包括等待子操作的时间)。例如,对于排序操作,这个时间包含读取数据、排序计算的耗时。

2. 2. rows=35

  • 含义:该操作节点 实际输出的行数

  • 对比参考:执行计划中通常会有 (rows=XX)估算行数(基于表统计信息)。例如:sql

Filter  (cost=10.00..20.00 rows=10) (actual time=0.016..0.073 rows=35)


这里优化器预估会返回 10 行,但实际返回了 35 行。
估算与实际行数差异较大 可能导致优化器选择次优计划(例如选择全表扫描而非索引扫描)。

3. 3. loops=1

  • 含义:该操作节点 实际执行的次数

  • 常见场景

  • loops=1:操作只执行一次(例如全表扫描、单次聚合)。

  • loops>1:操作被重复执行(例如在嵌套循环连接中,内层表的扫描会执行多次)。例如:sql

->  Seq Scan on inner_table  (actual time=0.01..0.05 rows=10 loops=5)


这里对内层表扫描了 5 次,每次返回 10 行,总计返回
10 × 5 = 50 行。

如果估算和真实执行的差距有点大,那我们这时候要手动收集下统计信息了analyze lineitem

(三) 阅读执行计划的顺序

PostgreSQL执行计划的阅读顺序遵循从上到下、从外层操作到内层操作的逻辑,但实际执行时是从叶子节点(最底层扫描)开始执行,逐层向上汇总结果。

  1. 执行计划节点类型