131

MySQL 的解释输出非常简单。PostgreSQL 有点复杂。我也找不到解释它的好资源。

你能描述一下确切的解释是什么,或者至少给我指出一个好的资源的方向吗?

4

7 回答 7

119

我总是感到困惑的部分是启动成本与总成本。每次我忘记它时,我都会谷歌一下,这让我回到了这里,这并不能解释其中的区别,这就是我写这个答案的原因。这是我从PostgresEXPLAIN文档中收集到的内容,根据我的理解进行了解释。

以下是管理论坛的应用程序的示例:

EXPLAIN SELECT * FROM post LIMIT 50;

Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

以下是 PgAdmin 的图形解释:

第一个查询的图形解释

(当您使用 PgAdmin 时,您可以将鼠标指向某个组件以阅读成本详细信息。)

成本表示为一个元组,例如LIMITiscost=0.00..3.39的成本和顺序扫描的成本postcost=0.00..15629.12。元组中的第一个数字是启动成本,第二个数字是总成本。因为我使用EXPLAIN而不是EXPLAIN ANALYZE,这些成本是估计值,而不是实际测量值。

  • 启动成本是一个棘手的概念。它不仅仅代表该组件开始之前的时间量。它表示从组件开始执行(读取数据)到组件输出其第一行之间的时间量。
  • 总成本是组件的整个执行时间,从开始读取数据到完成写入输出。

复杂的是,每个“父”节点的成本包括其子节点的成本。在文本表示中,树由缩进表示,例如LIMIT是父节点并且Seq Scan是其子节点。在 PgAdmin 表示中,箭头从子节点指向父节点——数据流的方向——如果你熟悉图论,这可能是违反直觉的。

文档说成本包括所有子节点,但请注意,父节点的总成本3.39远小于其子节点的总成本15629.12。总成本不包括在内,因为像这样的组件LIMIT不需要处理其全部输入。请参阅Postgres文档EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;中的示例。EXPLAIN

在上面的示例中,两个组件的启动时间都为零,因为两个组件都不需要在开始写入行之前进行任何处理:顺序扫描读取表的第一行并将其发出。读取它的LIMIT第一行,然后发出它。

组件何时需要进行大量处理才能开始输出任何行?有很多可能的原因,但让我们看一个明显的例子。这是之前的相同查询,但现在包含一个ORDER BY子句:

EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;

Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

并以图形方式:

第二个查询的图形解释

再一次,顺序扫描post没有启动成本:它立即开始输出行。但是排序的启动成本很高23283.24,因为它必须先对整个表进行排序,然后才能输出单行。排序的总成本23859.27仅略高于启动成本,这反映了一个事实,即一旦对整个数据集进行了排序,排序后的数据就可以非常快速地发出。

请注意, 的启动时间LIMIT 23283.24恰好等于排序的启动时间。这并不是因为LIMIT它本身具有很高的启动时间。它本身实际上具有零启动时间,但EXPLAIN汇总了每个父项的所有子项成本,因此LIMIT启动时间包括其子项的总启动时间。

这种成本汇总可能会导致难以理解每个单独组件的执行成本。例如,我们的LIMIT启动时间为零,但乍一看并不明显。出于这个原因,其他几个人链接到了explain.depesz.com,这是一个由 Hubert Lubaczewski(又名 depesz)创建的工具,它EXPLAIN通过从父母成本中减去孩子成本来帮助理解。他在一篇关于他的工具的简短博客文章中提到了其他一些复杂性。

于 2016-02-19T17:10:57.830 回答
50

Explaining_EXPLAIN.pdf也可以提供帮助。

于 2008-09-23T15:48:04.463 回答
44

它从最缩进到最不缩进执行,我相信从计划的底部到顶部。(因此,如果有两个缩进部分,页面下方的一个首先执行,然后当它们遇到另一个时执行,然后执行连接它们的规则。)

这个想法是,在每一步都有 1 或 2 个数据集到达并通过某种规则进行处理。如果只有一个数据集,则对该数据集执行该操作。(例如,扫描索引以找出您想要的行、过滤数据集或对其进行排序。)如果有两个,则这两个数据集是进一步缩进的两个东西,它们由您看到的规则连接。大多数规则的含义可以相当容易地猜到(特别是如果您以前阅读过一堆解释计划),但是您可以尝试通过查看文档或(更容易)将短语放入来验证单个项目谷歌连同一些关键字,如EXPLAIN.

这显然不是一个完整的解释,但它提供了足够的上下文,您通常可以找出您想要的任何内容。例如,考虑来自实际数据库的这个计划:

explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';

------------------------------------------------------------------------------------------------------------------------------------------------------------

 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)

尝试自己阅读它,看看它是否有意义。

我读到的是数据库首先扫描id_orderitem_productid索引,使用它来查找它想要的行orderitem,然后使用快速排序对该数据集进行排序(如果数据不适合 RAM,则使用的排序将改变),然后将其放在一边。

接下来,它扫描orditematt_attributeid_idx以找到它想要的行orderitemattribute,然后使用快速排序对该数据集进行排序。

然后它获取两个数据集并将它们合并。(合并连接是一种“压缩”操作,它并行遍历两个排序的数据集,当它们匹配时发出连接的行。)

正如我所说,您从内部到外部,从下到上进行计划。

于 2008-09-22T22:59:21.567 回答
24

还有一个可用的在线帮助工具Depesz,它将突出显示分析结果中昂贵部分的位置。

也有一个,这是相同的结果,这对我来说更清楚问题出在哪里。

于 2008-09-24T10:47:15.443 回答
14

PgAdmin将向您显示解释计划的图形表示。在两者之间来回切换确实可以帮助您理解文本表示的含义。但是,如果您只是想知道它要做什么,您也许可以一直使用 GUI。

于 2008-09-23T17:39:43.837 回答
6

PostgreSQL 的官方文档对如何理解 explain 的输出提供了一个有趣的、详尽的解释。

于 2013-03-18T00:09:42.547 回答
0

如果你安装了 pgadmin,会有一个解释按钮,除了给出文本输出之外,还可以绘制正在发生的事情的图表,显示过滤器、排序和子集合并,我发现这些对于查看正在发生的事情非常有用。

于 2008-09-25T13:15:58.837 回答