Eva*_*oll 19 postgresql sql-execution-plan
今天我花了一个多小时在一个我无法理解的查询计划上迷惑自己.查询是一个UPDATE,它根本就不会运行.完全陷入僵局:pg_locks表明它也没有等待任何事情.现在,我不认为自己是最好或最差的查询计划读者,但我发现这个非常困难.我想知道怎么读这些?为了找出错误,是否有Pg aces遵循的方法?
我打算再问一个关于如何解决这个问题的问题,但是现在我正在具体谈到如何阅读这些类型的计划.
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)
Run Code Online (Sandbox Code Playgroud)
这个查询计划的问题 - 我相信我理解 - 可能是最好的说法RhodiumToad(他在这方面肯定更好,所以我敢打赌他的解释更好)irc://irc.freenode.net/#postgresql:
哦,该计划可能是灾难性的,该计划的问题是它为每一行运行一个非常昂贵的hashjoin 问题是来自另一个连接的rows = 1估计,并且规划者认为可以在内部放置一个非常昂贵的查询一个nestloop的路径,其中估计外部路径只返回一行.显然,由于计划者的估计,昂贵的部分只会运行一次,但这有一个明显的趋势,在实践中真正陷入困境,问题是规划师理想地认为自己的估计,规划师需要知道"估计"之间的差异返回1行"和"不可能返回超过1行"但它根本不清楚如何将其合并到现有代码中
他继续说:
它可以影响任何连接,但通常最有可能连接子查询
现在当我读到这个计划时,我注意到的第一件事是Nested Loop Anti Join,这需要花费169,413(我会坚持上限).这种反连接中断下来的结果Nested Loop在成本31,738和的结果Hash Join在一个成本137,424.现在,137,424是很多大于31,738所以我知道这个问题是哈希联接.
然后我继续EXPLAIN ANALYZE查询之外的哈希加入段.它在7秒内执行.我确定(lot_id,vin)和(co.code,和v.code)上有索引 - 有.我单独禁用seq_scan并hashjoin注意到速度增加不到2秒.不足以说明一小时后它没有进展的原因.
但是,毕竟这是我完全错了!是的,它是查询的较慢部分,但因为rows="1"位(我认为它在上面Nested Loop Anti Join).这是规划师错误估计行数的错误(缺乏能力)?我怎么读到这个得出同样的结论RhodiumToad呢?
是不是rows="1"应该引发我解决这个问题?
我确实VACUUM FULL ANALYZE在所有涉及的表上运行,这是Postgresql 8.4.
Ant*_*sma 23
看到这样的问题需要一些关于事情可能出错的经验.但是要在查询计划中查找问题,请尝试从内到外验证生成的计划,检查行数估计是否合理,成本估算是否与花费的时间相匹配.顺便说一句.两个成本估算不低于上限,第一个是产生第一排产出的估计成本,第二个是估计的总成本,详见解释文档,还有一些可用的计划文档.它还有助于了解不同的访问方法如何工作.作为起点,维基百科有关于嵌套循环,散列和合并连接的信息.
在您的示例中,您将从以下开始:
-> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36)
Filter: (name IS NULL)
Run Code Online (Sandbox Code Playgroud)
运行EXPLAIN ANALYZE SELECT * FROM options WHERE name IS NULL;并查看返回的行是否与估计值匹配.2关系通常不是问题,你试图发现数量级的差异.
然后查看EXPLAIN ANALYZE SELECT * FROM vehicles WHERE date_sold IS NULL;返回预期的行数.
然后上升到哈希联接的一个级别:
-> 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))
Run Code Online (Sandbox Code Playgroud)
查看EXPLAIN ANALYZE SELECT * FROM vehicles AS iv INNER JOIN options io ON (io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text) WHERE iv.date_sold IS NULL AND io.name IS NULL;结果是否为229行.
再添加一个级别INNER JOIN options co ON (co.fkey_style = iv.chrome_styleid) AND (co.code = io.code),预计只返回一行.这可能是问题所在,因为如果行的实际数字从1变为100,则遍历包含嵌套循环的内循环的总成本估计值将减少100倍.
计划者所犯的潜在错误可能是它期望加入的两个谓词co彼此独立并使其选择性倍增.虽然实际上它们可能是高度相关的,并且选择性更接近MIN(s1,s2)而不是s1*s2.