EXPLAIN — 显示语句的执行计划
EXPLAIN [ (option
[, ...] ) ]statement
whereoption
can be one of: ANALYZE [boolean
] VERBOSE [boolean
] COSTS [boolean
] SETTINGS [boolean
] GENERIC_PLAN [boolean
] BUFFERS [boolean
] SERIALIZE [ { NONE | TEXT | BINARY } ] WAL [boolean
] TIMING [boolean
] SUMMARY [boolean
] MEMORY [boolean
] FORMAT { TEXT | XML | JSON | YAML }
此命令显示 PostgreSQL 规划器为给定语句生成的执行计划。执行计划显示语句引用的表将如何被扫描——通过普通顺序扫描、索引扫描等——如果引用了多个表,将使用什么连接算法来整合来自每个输入表的必需行。
显示中最关键的部分是估计的语句执行成本,这是规划器对运行该语句所需时间的猜测(以任意的成本单位衡量,但通常表示磁盘页面读取)。实际上显示了两个数字:返回第一行之前的启动成本,以及返回所有行的总成本。对于大多数查询,总成本才是重要的,但在像 EXISTS
中的子查询这样的上下文中,规划器会选择最小的启动成本而不是最小的总成本(因为执行器将在获取一行后停止)。此外,如果您使用 LIMIT
子句限制要返回的行数,规划器会在端点成本之间进行适当的插值,以估计哪个计划才是真正最便宜的。
使用 ANALYZE
选项会实际执行该语句,而不仅仅是计划。然后,执行时间统计信息将被添加到显示中,包括每个计划节点实际花费的总时间(以毫秒为单位)以及它实际返回的总行数。这有助于了解规划器的估计是否接近实际情况。
请记住,使用 ANALYZE
选项时,语句实际上会被执行。尽管 EXPLAIN
会丢弃 SELECT
会返回的任何输出,但语句的其他副作用会照常发生。如果您想在不影响数据的情况下对 INSERT
、UPDATE
、DELETE
、MERGE
、CREATE TABLE AS
或 EXECUTE
语句使用 EXPLAIN ANALYZE
,请使用此方法
BEGIN; EXPLAIN ANALYZE ...; ROLLBACK;
ANALYZE
执行命令并显示实际运行时间和其它统计信息。此参数默认为 FALSE
。
VERBOSE
显示有关计划的附加信息。具体来说,包括计划树中每个节点的输出列列表,对表和函数名进行模式限定,在表达式中始终用范围表别名标记变量,以及始终打印显示了统计信息的每个触发器的名称。如果已计算查询标识符,则还会显示该标识符,有关详细信息,请参阅 compute_query_id。此参数默认为 FALSE
。
COSTS
包括有关每个计划节点的估计启动成本和总成本,以及估计的行数和每行的估计宽度。此参数默认为 TRUE
。
SETTINGS
包括有关配置参数的信息。具体来说,包括影响查询规划但值与内置默认值不同的选项。此参数默认为 FALSE
。
GENERIC_PLAN
允许语句包含参数占位符(如 $1
),并生成一个不依赖于这些参数值的通用计划。有关通用计划以及支持参数的语句类型,请参阅 PREPARE
。此参数不能与 ANALYZE
一起使用。它默认为 FALSE
。
BUFFERS
包括有关缓冲区使用情况的信息。具体来说,包括命中(hit)、读取(read)、脏(dirtied)和写入(written)的共享块数量,命中、读取、脏和写入的本地块数量,读取和写入的临时块数量,以及读取和写入数据文件块、本地块和临时文件块所花费的时间(以毫秒为单位),前提是 track_io_timing 已启用。 命中 意味着在需要时由于块已在缓存中而避免了读取。共享块包含来自常规表和索引的数据;本地块包含来自临时表和索引的数据;而临时块包含在排序、哈希、物化计划节点等情况下使用的短期工作数据。 脏 块的数量表示在此查询中被更改的先前未修改的块的数量;而 写入 块的数量表示在此后端在查询处理过程中从缓存中逐出的先前已脏的块的数量。上层节点显示的块数包括其所有子节点使用的块数。在文本格式中,只打印非零值。当使用 ANALYZE
时,会自动包含缓冲区信息。
SERIALIZE
包括有关将查询的输出数据 序列化 的成本信息,即将其转换为文本或二进制格式以发送到客户端。如果数据类型输出函数开销很大,或者如果TOASTed 值必须从行外存储中获取,这可能是常规查询执行过程中花费时间的重要部分。 EXPLAIN
的默认行为 SERIALIZE NONE
不执行这些转换。如果指定了 SERIALIZE TEXT
或 SERIALIZE BINARY
,则执行相应的转换,并测量所花费的时间(除非指定了 TIMING OFF
)。如果还指定了 BUFFERS
选项,那么转换中涉及的任何缓冲区访问也会被计算在内。但在任何情况下,EXPLAIN
都不会实际将结果数据发送到客户端;因此,无法通过这种方式调查网络传输成本。序列化只能在同时启用 ANALYZE
时启用。如果 SERIALIZE
不带参数,则假定为 TEXT
。
WAL
包括有关 WAL 记录生成的信息。具体来说,包括记录数、完整页面映像(fpi)数、WAL 生成的字节数以及 WAL 缓冲区变满的次数。在文本格式中,只打印非零值。此参数只能与 ANALYZE
一起使用。它默认为 FALSE
。
TIMING
在输出中包含实际的启动时间和每个节点所花费的时间。反复读取系统时钟的开销会显著减慢某些系统的查询速度,因此,当只需要实际行数而不关注精确时间时,将此参数设置为 FALSE
可能很有用。即使通过此选项关闭了节点级计时,整个语句的运行时间总是会被测量。此参数只能与 ANALYZE
一起使用。它默认为 TRUE
。
SUMMARY
在查询计划之后包含摘要信息(例如,总计的计时信息)。当使用 ANALYZE
时,默认会包含摘要信息,但否则默认不包含,但可以使用此选项启用。 EXPLAIN EXECUTE
中的规划时间包括从缓存中获取计划所需的时间以及重新规划所需的时间(如果需要)。
MEMORY
包括有关查询规划阶段内存消耗的信息。具体来说,包括规划器内存结构使用的精确存储量,以及考虑分配开销的总内存。此参数默认为 FALSE
。
FORMAT
指定输出格式,可以是 TEXT、XML、JSON 或 YAML。非文本输出包含与文本输出格式相同的信息,但更易于程序解析。此参数默认为 TEXT
。
boolean
指定是否应打开或关闭选定的选项。您可以编写 TRUE
、ON
或 1
来启用选项,编写 FALSE
、OFF
或 0
来禁用选项。boolean
值也可以省略,在这种情况下假定为 TRUE
。
statement
任何你想查看其执行计划的 SELECT
、INSERT
、UPDATE
、DELETE
、MERGE
、VALUES
、EXECUTE
、DECLARE
、CREATE TABLE AS
或 CREATE MATERIALIZED VIEW AS
语句。
命令的结果是对为 statement
选择的计划的文本描述,可选择性地附带执行统计信息。第 14.1 节 描述了提供的信息。
为了让 PostgreSQL 查询规划器在优化查询时做出合理的决策,查询中使用的所有表的 pg_statistic
数据应是最新的。通常 autovacuum 守护进程 会自动处理此问题。但是,如果一个表的 DDL 最近有substantial 变化,您可能需要执行手动 ANALYZE
,而不是等待 autovacuum 跟上这些 DDL。
为了测量执行计划中每个节点的运行成本,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=10.77..10.87 rows=10 width=12) (actual time=0.043..0.044 rows=10.00 loops=1) Group Key: foo Batches: 1 Memory Usage: 24kB Buffers: shared hit=4 -> Index Scan using test_pkey on test (cost=0.29..10.27 rows=99 width=8) (actual time=0.009..0.025 rows=99.00 loops=1) Index Cond: ((id > 100) AND (id < 200)) Index Searches: 1 Buffers: shared hit=4 Planning Time: 0.244 ms Execution Time: 0.073 ms (10 rows)
当然,这里显示的具体数字取决于相关表的实际内容。还要注意,由于规划器的改进,数字,甚至选择的查询策略,在 PostgreSQL 的不同版本之间可能会有所不同。此外,ANALYZE
命令使用随机抽样来估算数据统计信息;因此,即使表中数据的实际分布没有改变,成本估算在每次运行 ANALYZE
后也可能发生变化。
请注意,前面的示例显示了一个针对 EXECUTE
中给定的特定参数值的“custom”计划。我们也可能希望看到参数化查询的通用计划,这可以通过 GENERIC_PLAN
来实现
EXPLAIN (GENERIC_PLAN) SELECT sum(bar) FROM test WHERE id > $1 AND id < $2 GROUP BY foo; QUERY PLAN ------------------------------------------------------------------------------- HashAggregate (cost=26.79..26.89 rows=10 width=12) Group Key: foo -> Index Scan using test_pkey on test (cost=0.29..24.29 rows=500 width=8) Index Cond: ((id > $1) AND (id < $2)) (4 rows)
在这种情况下,解析器正确推断出 $1
和 $2
应该与 id
具有相同的数据类型,因此 PREPARE
缺少参数类型信息不成问题。在其他情况下,可能需要显式指定参数符号的类型,可以通过强制类型转换来实现,例如
EXPLAIN (GENERIC_PLAN) SELECT sum(bar) FROM test WHERE id > $1::integer AND id < $2::integer GROUP BY foo;
SQL 标准中没有定义 EXPLAIN
语句。
在 PostgreSQL 版本 9.0 之前使用了以下语法,并且仍然支持
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
请注意,在此语法中,选项必须按所示顺序精确指定。
如果您在文档中发现任何不正确、与您对特定功能的体验不符或需要进一步说明的内容,请使用 此表单 来报告文档问题。