pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
EXPLAIN — show the execution plan of a statement
EXPLAIN [ (option[, ...] ) ]statementEXPLAIN [ ANALYZE ] [ VERBOSE ]statementwhereoptioncan be one of: ANALYZE [boolean] VERBOSE [boolean] COSTS [boolean] BUFFERS [boolean] FORMAT { TEXT | XML | JSON | YAML }
这个命令显示PostgreSQL规划器为给定语句生成的执行计划。执行计划会显示将如何扫描该语句引用的表 — 例如普通顺序扫描、索引扫描等 —,如果引用了多个表,还会显示将使用哪些连接算法来汇集每个输入表中的所需行。
显示结果中最关键的部分是语句执行代价的估计值,它是规划器对运行该语句需要多长时间的猜测(以任意代价单位衡量,但按惯例表示磁盘页面抓取次数)。实际上会显示两个数字:返回第一行之前的启动代价,以及返回全部行的总代价。对大多数查询来说,总代价才是关键;但在某些场景中,例如EXISTS中的子查询,规划器会选择启动代价最小而不是总代价最小的计划(因为无论如何执行器都会在得到一行后停止)。此外,如果你用LIMIT子句限制返回的行数,规划器会在端点代价之间作适当插值,以估计哪个计划实际上最便宜。
ANALYZE 选项导致语句被真正执行,而不仅仅是规划。每个计划节点内部耗费的总时间(以毫秒计)以及它实际返回的总行数会被加入显示。这有助于查看规划器的估计是否接近现实。
请记住,使用 ANALYZE 选项时,语句会被实际执行。虽然 EXPLAIN 会丢弃 SELECT 返回的任何输出,但语句的其他副作用仍会照常发生。如果希望使用 EXPLAIN ANALYZE 分析 INSERT, UPDATE, DELETE, CREATE TABLE AS 或 EXECUTE 语句而不让命令影响数据,可以采用以下方法:
BEGIN; EXPLAIN ANALYZE ...; ROLLBACK;
不使用括号包围选项列表时,只能指定ANALYZE和VERBOSE选项,而且只能按这个顺序指定。在PostgreSQL 9.0 之前,只支持这种不带括号的语法。预计所有新选项都将仅在带括号的语法中得到支持。
ANALYZE执行命令并显示实际的运行时间。此参数默认为 FALSE。
VERBOSE显示有关计划的附加信息。具体包括:计划树中每个节点的输出列列表、模式限定的表名和函数名、总是用其范围表别名标注表达式中的变量,以及总是打印显示统计信息的每个触发器名称。此参数默认为FALSE。
COSTS包含每个计划节点的估计启动代价和总代价,以及估计行数和每行的估计宽度。此参数默认为TRUE。
BUFFERS包含缓冲区使用情况的信息。具体来说,包含共享块命中、读和写的次数,本地块命中、读和写的次数,以及临时块读和写的次数。共享块、本地块和临时块分别包含表和索引、临时表和临时索引,以及排序和物化计划中使用的磁盘块。上层节点显示的块数包括其所有子节点使用的块。在文本格式中,只打印非零值。此参数只能与 ANALYZE 参数一起使用。默认为 FALSE。
FORMAT指定输出格式,可以是 TEXT、XML、JSON 或 YAML。非文本输出包含与文本输出相同的信息,但更容易被程序解析。此参数默认为TEXT。
boolean指定所选选项应开启还是关闭。可以写TRUE、ON或1来启用选项,写FALSE、OFF或0来禁用它。boolean值也可以省略,在这种情况下假定其值为TRUE。
statement任何你希望查看其执行计划的SELECT、INSERT、UPDATE、DELETE、VALUES、EXECUTE、DECLARE或CREATE TABLE AS语句。
关于 PostgreSQL 中优化器对代价信息的使用,文档很少。更多信息参见 第 14.1 节。
为了让 PostgreSQL 查询规划器在优化查询时能做出合理知情的决定,应当运行 ANALYZE 语句来记录表中数据分布的统计信息。如果你没有这样做(或者自上次运行 ANALYZE 以来表中数据的统计分布发生了显著变化),估计的代价不太可能符合查询的真实属性,因此可能选出一个较差的查询计划。
为了测量执行计划中每个节点的运行时代价,EXPLAIN ANALYZE 的当前实现可能给查询执行增加相当大的剖析开销。因此,对某个查询运行 EXPLAIN ANALYZE 有时可能比正常执行该查询花费显著更长的时间。开销的大小取决于查询的性质。
要显示一个只有单个integer列且包含 10000 行的表上的简单查询计划:
EXPLAIN SELECT * FROM foo;
QUERY PLAN
---------------------------------------------------------
Seq Scan on foo (cost=0.00..155.00 rows=10000 width=4)
(1 row)
下面是同一查询,但使用 JSON 输出格式:
EXPLAIN (FORMAT JSON) SELECT * FROM foo;
QUERY PLAN
--------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Seq Scan",+
"Relation Name": "foo", +
"Alias": "foo", +
"Startup Cost": 0.00, +
"Total Cost": 155.00, +
"Plan Rows": 10000, +
"Plan Width": 4 +
} +
} +
]
(1 row)
如果存在索引,并且我们使用了带有可索引WHERE条件的查询,EXPLAIN可能会显示不同的计划:
EXPLAIN SELECT * FROM foo WHERE i = 4;
QUERY PLAN
--------------------------------------------------------------
Index Scan using fi on foo (cost=0.00..5.98 rows=1 width=4)
Index Cond: (i = 4)
(2 rows)
下面是同一查询,但采用 YAML 格式:
EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4';
QUERY PLAN
-------------------------------
- Plan: +
Node Type: "Index Scan" +
Scan Direction: "Forward"+
Index Name: "fi" +
Relation Name: "foo" +
Alias: "foo" +
Startup Cost: 0.00 +
Total Cost: 5.98 +
Plan Rows: 1 +
Plan Width: 4 +
Index Cond: "(i = 4)"
(1 row)
XML 格式留给读者自行练习。
下面是同一计划,但隐藏了代价估计:
EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4;
QUERY PLAN
----------------------------
Index Scan using fi on foo
Index Cond: (i = 4)
(2 rows)
下面是使用聚合函数的查询的查询计划示例:
EXPLAIN SELECT sum(i) FROM foo WHERE i < 10;
QUERY PLAN
---------------------------------------------------------------------
Aggregate (cost=23.93..23.93 rows=1 width=4)
-> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4)
Index Cond: (i < 10)
(3 rows)
下面是使用 EXPLAIN EXECUTE 显示预备查询执行计划的示例:
PREPARE query(int, int) AS SELECT sum(bar) FROM test
WHERE id > $1 AND id < $2
GROUP BY foo;
EXPLAIN ANALYZE EXECUTE query(100, 200);
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=9.54..9.54 rows=1 width=8) (actual time=0.156..0.161 rows=11 loops=1)
Group Key: foo
-> Index Scan using test_pkey on test (cost=0.29..9.29 rows=50 width=8) (actual time=0.039..0.091 rows=99 loops=1)
Index Cond: ((id > $1) AND (id < $2))
Planning time: 0.197 ms
Execution time: 0.225 ms
(6 rows)
当然,这里显示的具体数字取决于所涉及表的实际内容。还要注意,由于规划器的改进,这些数字甚至所选查询策略在PostgreSQL的不同版本之间都可能发生变化。此外,ANALYZE命令使用随机采样来估计数据统计信息;因此,即使表中数据的实际分布没有变化,在重新运行一次ANALYZE之后,代价估计也可能发生变化。
SQL 标准中没有定义EXPLAIN语句。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。