什么是PostgreSQL解释告诉我到底是什么?

时间:2008-09-22 20:08:11

标签: postgresql

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

你能描述一下究竟解释的是什么,或者至少指出了一个好资源的方向吗?

7 个答案:

答案 0 :(得分:51)

Explaining_EXPLAIN.pdf也可以提供帮助。

答案 1 :(得分:43)

我总觉得令人困惑的部分是启动成本与总成本。我每次忘记它都会谷歌这个,这让我回到这里,这并不能解释其中的差异,这就是我写这个答案的原因。这是我从Postgres EXPLAIN documentation收集到的,根据我的理解解释。

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

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的图解说明:

graphical explanation of first query

(当您使用PgAdmin时,您可以将鼠标指向某个组件以阅读费用明细。)

费用表示为元组,例如LIMIT的费用为cost=0.00..3.39,顺序扫描post的费用为cost=0.00..15629.12。元组中的第一个数字是启动成本,第二个数字是总成本。由于我使用EXPLAIN而非EXPLAIN ANALYZE,因此这些费用是估算值,而不是实际值。

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

作为一个并发症,每个父母"节点的成本包括其子节点的成本。在文本表示中,树由缩进表示,例如, LIMIT是父节点,Seq Scan是其子节点。在PgAdmin表示中,箭头指向从子到父 - 数据流的方向 - 如果您熟悉图论,这可能是违反直觉的。

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

在上面的示例中,两个组件的启动时间均为零,因为在开始编写行之前,两个组件都不需要执行任何处理:顺序扫描会读取表的第一行并发出它。 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)

以图形方式:

graphical explanation of second query

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

请注意,LIMIT 23283.24的启动时间与排序的启动时间完全相同。这不是因为LIMIT本身具有较高的启动时间。它本身实际上没有启动时间,但EXPLAIN汇总了每个父项的所有子成本,因此LIMIT启动时间包括其子项的总启动时间。

这种成本汇总使得很难理解每个组件的执行成本。例如,我们的LIMIT启动时间为零,但乍一看并不明显。出于这个原因,其他几个人与explain.depesz.com相关联,这是一个由Hubert Lubaczewski(a.k.a. depesz)创建的工具,它帮助理解EXPLAIN - 除其他外 - 从父成本中减去儿童成本。他在a short blog post中提到了他的工具中的一些其他复杂性。

答案 2 :(得分:40)

它从大多数缩进到最小缩进执行,我相信从计划的底部到顶部。 (因此,如果有两个缩进的部分,页面下方的一个首先执行,那么当它们遇到另一个执行时,则执行加入它们的规则。)

这个想法是,每个步骤都有1个或2个数据集到达并由某个规则处理。如果只有一个数据集,则对该数据集执行该操作。 (例如,扫描索引以确定所需的行,过滤数据集或对其进行排序。)如果是两个,则两个数据集是进一步缩进的两个数据集,并且它们由您看到的规则连接。大多数规则的含义可以合理地容易猜到(特别是如果你之前已经阅读过一堆解释计划),但是你可以尝试通过查看文档来验证单个项目,或者(更简单)通过将短语投入到Google以及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,然后使用快速排序对该数据集进行排序。

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

正如我所说,你通过计划内部部分到外部部分,从下到上。

答案 3 :(得分:21)

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

还有一个,这里是same results,这让我更清楚问题出在哪里。

答案 4 :(得分:13)

PgAdmin将显示解释计划的图形表示。在两者之间来回切换可以真正帮助您理解文本表示的含义。但是,如果您只是想知道它的用途,您可以随时使用GUI。

答案 5 :(得分:6)

PostgreSQL's official documentation提供了有关如何理解解释输出的有趣,彻底的解释。

答案 6 :(得分:0)

如果您安装了pgadmin,那么就会有一个Explain按钮,并提供文本输出,绘制正在发生的事情的图表,显示过滤器,排序和子集合并,我发现这些合并对于查看正在发生的事情非常有用。