“我怎么能?”;“想得更好”;读取PostgreSQL查询计划时?
今天我花了一个多小时在一个我无法理解的查询计划上困惑自己。该查询是一个“我怎么能?”;“想得更好”;读取PostgreSQL查询计划时?,postgresql,sql-execution-plan,Postgresql,Sql Execution Plan,今天我花了一个多小时在一个我无法理解的查询计划上困惑自己。该查询是一个更新,根本不会运行。完全陷入僵局:pg_locks显示它也没有等待任何东西。现在,我不认为自己是最好或最坏的查询计划读取器,但我发现这一个非常困难。我想知道人们是怎么读这些的?是否有Pg ACE遵循的方法来确定错误 我打算问另一个问题,关于如何解决这个问题,但现在我要特别谈谈如何阅读这些类型的计划 QUERY PLAN
更新
,根本不会运行。完全陷入僵局:pg_locks
显示它也没有等待任何东西。现在,我不认为自己是最好或最坏的查询计划读取器,但我发现这一个非常困难。我想知道人们是怎么读这些的?是否有Pg ACE遵循的方法来确定错误
我打算问另一个问题,关于如何解决这个问题,但现在我要特别谈谈如何阅读这些类型的计划
QUERY PLAN
--------------------------------------------------------------------------------------------
Nested Loop Anti Join (cost=47680.88..169413.12 rows=1 width=77)
Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name))
-> Nested Loop (cost=5301.58..31738.10 rows=1 width=81)
-> Hash Join (cost=5301.58..29722.32 rows=229 width=40)
Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
-> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36)
Filter: (name IS NULL)
-> Hash (cost=4547.33..4547.33 rows=36150 width=24)
-> Seq Scan on vehicles iv (cost=0.00..4547.33 rows=36150 width=24)
Filter: (date_sold IS NULL)
-> Index Scan using options_pkey on options co (cost=0.00..8.79 rows=1 width=49)
Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code))
-> Hash Join (cost=42379.30..137424.09 rows=16729 width=26)
Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text))
-> Seq Scan on vehicles v (cost=0.00..4547.33 rows=65233 width=24)
-> Hash (cost=20223.32..20223.32 rows=931332 width=44)
-> Seq Scan on options o (cost=0.00..20223.32 rows=931332 width=44)
(17 rows)
这个查询计划的问题——我相信我能理解——最好的说法是RhodiumToad
(他在这方面肯定更好,所以我打赌他的解释会更好)的irc://irc.freenode.net/#postgresql
:
哦,那个计划可能是灾难性的
该计划的问题在于,它为每一行运行一个非常昂贵的hashjoin
问题是来自另一个联接和
计划者认为,在nestloop的内部路径中放置一个非常昂贵的查询是可以的,外部路径估计只返回一行。
因为,很明显,根据规划师的估计,昂贵的部分只会运行一次
但这在实践中有一个明显的趋势,就是真的搞砸了
问题是规划者相信自己的估计
理想情况下,计划员需要知道“预计返回1行”和“不可能返回超过1行”之间的差异
但如何将其纳入现有代码中还不清楚
他接着说:
它可以影响任何连接,但通常最有可能是针对子查询的连接
现在,当我阅读这个计划时,我注意到的第一件事是嵌套循环反连接
,它的成本是169413
(我将坚持上限)。此反连接分解为嵌套循环的结果,代价为31738
,以及哈希连接的结果,代价为137424
。现在,137424
比31738
大得多,所以我知道问题出在散列连接上
然后我继续分析查询外部的散列连接段。它在7秒内执行。我确保(批号、vin)和(公司代码和v代码)上都有索引——确实有。我分别禁用了seq_scan
和hashjoin
,并注意到速度增加不到2秒。距离不够近,无法解释为什么一个小时后没有进展
但是,在这一切之后,我完全错了!是的,这是查询中较慢的部分,但因为行=“1”
位(我假定它位于嵌套循环反联接上)。这里是planner错误估计行数的错误(缺乏能力)?我该如何解读这篇文章才能得出与RhodiumToad相同的结论
难道仅仅是rows=“1”
触发了我的思考吗
我在所有涉及的表上运行了VACUUM FULL ANALYZE
,这是Postgresql 8.4。您分析了这些表吗?pg_的统计数据对这些表格有什么意义?查询计划基于统计数据,这些数据必须是确定的。你用的是什么版本?8.4
可以使用统计信息、页面数量、行数量以及postgresql.conf中Planner成本常量的设置来计算成本
工作记忆也涉及其中,它可能太低,迫使计划人员进行顺序扫描,以降低性能…要想解决此类问题,需要一些经验,了解哪里会出错。但要在查询计划中找到问题,请尝试从内到外验证生成的计划,检查行数估计是否正确,成本估计是否与花费的时间匹配。顺便说一句,这两个成本估算不是上下限,第一个是生产第一行输出的估算成本,第二个数字是估算总成本,有关详细信息,请参阅,还有一些可用的。了解不同的访问方法是如何工作的也很有帮助。作为起点,维基百科提供了关于和的信息
在您的示例中,您可以从以下内容开始:
-> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36)
Filter: (name IS NULL)
运行EXPLAIN ANALYZE SELECT*FROM name为NULL的选项
并查看返回的行是否与估计值匹配。系数为2通常不是问题,您试图找出数量级的差异
然后参见EXPLAIN ANALYZE SELECT*FROM vehicles WHERE date_seal为空代码>返回预期的行数
然后上升一级到哈希联接:
-> Hash Join (cost=5301.58..29722.32 rows=229 width=40)
Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
查看是否EXPLAIN ANALYZE SELECT*FROM vehicles AS iv internal JOIN options io ON(io.lot\U id=iv.lot\U id)和((io.vin)::text=(iv.vin)::text),其中iv.date\U SALL为空,io.name为空代码>结果为229行
再向上一级添加内部连接选项co-ON(co.fkey\u style=iv.chrome\u styleid)和(co.code=io.code)
,预计只返回一行。这可能是问题所在,因为如果行的实际numebr从1变为100,则遍历包含嵌套循环的内部循环的总成本估计值将减少100倍
规划者正在犯的潜在错误可能是,它期望用于加入co
的两个谓词相互独立,并增加它们的选择性。实际上,它们可能高度相关,选择性更接近于MIN(s1,s2),而不是s1*s2。我对所有涉及的表都运行了真空全分析
,这是Postgresql 8.4。这是一个很好的答案,但当你说加入co
时,你指的是哪一个?我相信对这个问题的解释是因为