PostgreSQL 解释到底告诉我啥?
Posted
技术标签:
【中文标题】PostgreSQL 解释到底告诉我啥?【英文标题】:What is PostgreSQL explain telling me exactly?PostgreSQL 解释到底告诉我什么? 【发布时间】:2010-09-12 03:34:04 【问题描述】:mysql 的解释输出非常简单。 PostgreSQL 有点复杂。我也找不到很好的资源来解释它。
你能描述一下explain到底在说什么,或者至少给我指出一个好的资源的方向吗?
【问题讨论】:
【参考方案1】:我总是感到困惑的部分是启动成本与总成本。每次我忘记它时,我都会谷歌一下,这让我回到了这里,这并不能解释其中的区别,这就是我写这个答案的原因。这是我从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 的图形解释:
(当您使用 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)
并以图形方式:
再一次,post
上的顺序扫描没有启动成本:它立即开始输出行。但是这种排序的启动成本很高23283.24
,因为它必须对整个表进行排序,然后才能输出一行。排序23859.27
的总成本仅略高于启动成本,反映了一旦整个数据集排序完毕,排序后的数据可以很快发出。
注意LIMIT
23283.24
的启动时间正好等于排序的启动时间。这并不是因为LIMIT
本身的启动时间很长。实际上它本身的启动时间为零,但是EXPLAIN
汇总了每个父节点的所有子节点成本,因此LIMIT
启动时间包括其子节点的总启动时间。
这种成本汇总可能会让人难以理解每个单独组件的执行成本。例如,我们的LIMIT
的启动时间为零,但乍一看并不明显。出于这个原因,其他几个人与explain.depesz.com 相关联,这是一个由 Hubert Lubaczewski(又名 depesz)创建的工具,它通过从父母成本中减去儿童成本来帮助理解 EXPLAIN
。他在a short blog post 中提到了关于他的工具的其他一些复杂性。
【讨论】:
谢谢你。我还想添加一个解释可视化器,它可以更好地显示输出(imo)。 tatiyants.com/pev 很好的答案。您对启动成本包括返回第一行的时间的评论帮助我理解了为什么排序的启动成本不只是 15629.12。 "总成本是组件的整个执行时间," - 不,不是。成本值不代表“执行时间”。如果你使用explain (analyze)
,你只能看到执行时间
@a_horse_with_no_name 告诉我你没有阅读我的整篇文章而不告诉我你没有阅读我的整篇文章?
非常好的答案!【参考方案2】:
Explaining_EXPLAIN.pdf 也有帮助。
【讨论】:
我很奇怪为什么人们认为幻灯片是很好的技术文档。演讲视频可能会有所帮助,但该幻灯片的信息密度非常接近于零。在前六张幻灯片(占总数的 1/5)中,正好有 1 句技术内容:“• EXPLAIN 适用于任何 DML,而不仅仅是 SELECT(即 UPDATE、DELETE 和 INSERT)”。我最大的误解是“启动”时间是什么意思,而这大约 30 张幻灯片中的任何地方都没有解释。【参考方案3】:它从最大缩进到最小缩进执行,我相信从计划的底部到顶部。 (因此,如果有两个缩进部分,则页面下方的一个首先执行,然后当它们遇到另一个时执行,然后执行连接它们的规则。)
这个想法是,在每一步都有 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
中找到它想要的行,然后使用快速排序对该数据集进行排序。
然后它获取两个数据集并将它们合并。 (合并连接是一种“压缩”操作,它并行遍历两个排序的数据集,当它们匹配时发出连接的行。)
正如我所说,您从内部到外部,从下到上进行计划。
【讨论】:
【参考方案4】:还有一个在线帮助工具,Depesz,它将突出显示分析结果中昂贵部分的位置。
也有一个,这里是same results,这让我更清楚问题出在哪里。
【讨论】:
explain-analyze.info 似乎已关闭,但我同意 explain.depesz.com 非常有帮助。【参考方案5】:PgAdmin 将向您显示解释计划的图形表示。在两者之间来回切换确实可以帮助您理解文本表示的含义。但是,如果您只是想知道它要做什么,您也许可以一直使用 GUI。
【讨论】:
【参考方案6】:PostgreSQL's official documentation 就如何理解 explain 的输出提供了一个有趣而详尽的解释。
【讨论】:
【参考方案7】:如果您安装 pgadmin,则有一个解释按钮,除了提供文本输出之外,还可以绘制正在发生的事情的图表,显示过滤器、排序和子集合并,我认为这些对于查看正在发生的事情非常有用。
【讨论】:
以上是关于PostgreSQL 解释到底告诉我啥?的主要内容,如果未能解决你的问题,请参考以下文章