PostgreSQL确切解释了什么?


116

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

您能否描述确切解释的内容,或者至少将我指向好的资源的方向?

Answers:


50

Explaining_EXPLAIN.pdf也可以提供帮助。


64
我很奇怪,为什么人们认为滑轨可以提供良好的技术文档。演讲视频可能会有所帮助,但是该幻灯片的信息密度非常接近于零。在前六张幻灯片(占总数的1/5)中,技术内容恰好只有一句话:“•EXPLAIN可以对任何DML起作用,而不仅仅是SELECT(即UPDATE,DELETE和INSERT)。” 我最大的误解是“启动”时间的含义,在这30张幻灯片中没有任何地方解释。
Mark E. Haase

80

我总是感到困惑的部分是启动成本与总成本。每当我忘记它时,我都会使用Google,这使我回到这里,但并不能解释两者之间的区别,这就是我编写此答案的原因。这就是我从Postgres EXPLAIN文档中收集到的信息据我所知进行了解释。

这是管理论坛的应用程序中的示例:

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时,您可以将鼠标指向某个组件以读取费用明细。)

成本被表示为元组,例如,在成本LIMITcost=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,由休伯特Lubaczewski(又名depesz),有助于理解创建的工具EXPLAIN的-除其他事项外-减去从父成本孩子的费用。他在一篇有关他的工具的简短博客文章中提到了其他一些复杂性。


4
这次真是万分感谢。我还想添加一个解释可视化工具,以更好地显示输出(imo)。tatiyants.com/pev
乔纳森·波特

好答案。您对启动成本包括返回第一行的时间的评论帮助我理解了为什么Sort的启动成本不只是15629.12。
Joel Wigton

43

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

这个想法是,在每个步骤中,都有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,然后使用快速排序对数据集进行排序。

然后,它将两个数据集合并。(合并联接是一种“压缩”操作,其中它使两个排序的数据集并行移动,并在它们匹配时发出联接的行。)

就像我说的那样,您要遍历计划的内部到外部,从下到上。



13

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



0

如果安装了pgadmin,则有一个“解释”按钮,该按钮还为文本输出绘制了所发生的事情的图表,显示了我认为对了解所发生的事情非常有用的过滤器,排序和子集合并。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.