“我怎么能?”;“想得更好”;读取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
时,你指的是哪一个?我相信对这个问题的解释是因为