2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

EXPLAIN

EXPLAIN — 显示语句的执行计划

概要

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

where option 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 将返回的任何输出,但语句的其他副作用将照常发生。如果您希望在 INSERTUPDATEDELETEMERGECREATE TABLE ASEXECUTE 语句上使用 EXPLAIN ANALYZE 而不让命令影响您的数据,请使用此方法

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;

参数

ANALYZE

执行命令并显示实际运行时间和其他统计信息。此参数默认为 FALSE

VERBOSE

显示有关计划的其他信息。具体来说,包括计划树中每个节点的输出列列表、模式限定表和函数名称,始终用其范围表别名标记表达式中的变量,并始终打印每个触发器的名称(为此显示统计信息)。如果已计算查询标识符,则也会显示它,有关更多详细信息,请参阅 compute_query_id。此参数默认为 FALSE

COSTS

包括每个计划节点的估计启动和总成本信息,以及估计的行数和每行的估计宽度。此参数默认为 TRUE

SETTINGS

包括配置参数信息。具体来说,包括影响查询计划且值不同于内置默认值的选择项。此参数默认为 FALSE

GENERIC_PLAN

允许语句包含参数占位符(如 $1),并生成一个不依赖于这些参数值的通用计划。有关通用计划以及支持参数的语句类型,请参阅 PREPARE。此参数不能与 ANALYZE 一起使用。它默认为 FALSE

BUFFERS

包括缓冲区使用信息。具体来说,包括命中的共享块数、读取的块数、弄脏的块数和写入的块数,命中的本地块数、读取的块数、弄脏的块数和写入的块数,读取的临时块数和写入的块数,以及如果启用了 track_io_timing,则读取和写入数据文件块、本地块和临时文件块所花费的时间(以毫秒为单位)。命中 表示避免读取,因为在需要时已在缓存中找到该块。共享块包含来自常规表和索引的数据;本地块包含来自临时表和索引的数据;而临时块包含排序、哈希、Materialize 计划节点等情况下使用的短期工作数据。弄脏的块数指示此查询更改的先前未修改的块数;而写入的块数指示在查询处理期间此后端从缓存中逐出的先前弄脏的块数。上层节点显示的块数包括其所有子节点使用的块数。在文本格式中,仅打印非零值。此参数默认为 FALSE

SERIALIZE

包括有关序列化查询输出数据成本的信息,即将其转换为文本或二进制格式以发送到客户端。如果数据类型输出函数很昂贵,或者如果TOAST需要从行外存储中获取编辑的值,则这可能是常规执行查询所需时间的重要组成部分。 EXPLAIN 的默认行为 SERIALIZE NONE 不会执行这些转换。如果指定了 SERIALIZE TEXTSERIALIZE BINARY,则会执行相应的转换,并且会测量执行转换所花费的时间(除非指定了 TIMING OFF)。如果还指定了 BUFFERS 选项,则还会计算转换中涉及的任何缓冲区访问。但是,在任何情况下,EXPLAIN 实际上都不会将结果数据发送到客户端;因此,无法通过这种方式调查网络传输成本。仅当也启用了 ANALYZE 时,才能启用序列化。如果 SERIALIZE 没有参数,则假定为 TEXT

WAL

包括有关 WAL 记录生成的信息。具体来说,包括记录数、完整页面映像 (fpi) 数量以及生成的 WAL 量(以字节为单位)。在文本格式中,仅打印非零值。此参数仅在也启用了 ANALYZE 时才能使用。它默认为 FALSE

TIMING

在输出中包括实际启动时间和每个节点花费的时间。反复读取系统时钟的开销可能会在某些系统上显着降低查询速度,因此当只需要实际行数而不是精确时间时,将此参数设置为 FALSE 可能很有用。即使使用此选项关闭节点级计时,也会始终测量整个语句的运行时间。此参数仅在也启用了 ANALYZE 时才能使用。它默认为 TRUE

SUMMARY

在查询计划之后包含摘要信息(例如,汇总计时信息)。当使用 ANALYZE 时,默认情况下会包含摘要信息,否则默认情况下不会包含,但可以使用此选项启用。 EXPLAIN EXECUTE 中的计划时间包括从缓存中获取计划所需的时间以及重新计划(如果需要)所需的时间。

MEMORY

包括查询计划阶段的内存消耗信息。具体来说,包括计划程序内存中结构使用的精确存储量,以及考虑分配开销的总内存。此参数默认为 FALSE

FORMAT

指定输出格式,可以是 TEXT、XML、JSON 或 YAML。非文本输出包含与文本输出格式相同的信息,但程序更容易解析。此参数默认为 TEXT

布尔值

指定是否应打开或关闭所选选项。您可以编写 TRUEON1 以启用该选项,以及 FALSEOFF0 以禁用它。也可以省略布尔值,在这种情况下,假定为 TRUE

语句

任何 SELECTINSERTUPDATEDELETEMERGEVALUESEXECUTEDECLARECREATE TABLE ASCREATE MATERIALIZED VIEW AS 语句,您希望查看其执行计划。

输出

命令的结果是对为 语句 选择的计划的文本描述,可以选择使用执行统计信息进行注释。第 14.1 节 描述了提供的信息。

备注

为了允许 PostgreSQL 查询计划程序在优化查询时做出合理的明智决策,pg_statistic 数据应针对查询中使用的所有表保持最新。通常,自动清理守护程序 会自动处理此问题。但是,如果表的內容最近发生了重大变化,您可能需要手动执行 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=10.77..10.87 rows=10 width=12) (actual time=0.043..0.044 rows=10 loops=1)
   Group Key: foo
   Batches: 1  Memory Usage: 24kB
   ->  Index Scan using test_pkey on test  (cost=0.29..10.27 rows=99 width=8) (actual time=0.009..0.025 rows=99 loops=1)
         Index Cond: ((id > 100) AND (id < 200))
 Planning Time: 0.244 ms
 Execution Time: 0.073 ms
(7 rows)

当然,此处显示的具体数字取决于所涉及表的实际内容。还要注意,由于规划器改进,数字甚至所选的查询策略可能在 PostgreSQL 版本之间有所不同。此外,ANALYZE 命令使用随机抽样来估计数据统计信息;因此,即使表中数据的实际分布没有发生变化,成本估算也可能在 ANALYZE 重新运行后发生变化。

请注意,前面的示例显示了在 EXECUTE 中给定的特定参数值对应的 自定义 计划。我们可能还想查看参数化查询的通用计划,这可以通过 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

请注意,在此语法中,必须按所示顺序精确指定选项。

另请参阅

ANALYZE

提交更正

如果您在文档中发现任何不正确的内容,与您对特定功能的体验不符,或者需要进一步澄清,请使用 此表单 报告文档问题。