Kub*_*tek 6 sql-server execution-plan cardinality-estimates
这个问题是其他人问题的后续问题:尽管更新了统计信息和重新编译,但由于执行计划不同,添加 INNER JOIN 会破坏查询性能,为什么?
我的问题是关于执行计划的一小部分。
索引(节点 22)上的 Index Seek 运算符Events.nci_wi_...
估计返回 77191 行。这是嵌套循环运算符(节点 19)的外部输入。内部输入是Locations.UC_LocationID
(节点 23)上的索引查找。该节点的估计执行次数为2614。
我天真的理解是,由于嵌套循环在外部被输入 77191 行,因此它别无选择,只能执行内部运算符同样多次(至少使用内部连接逻辑运算符)。为什么这里有区别呢?
嵌套循环本身估计会返回 40 行。这比外部输入的行数少三个数量级。
我知道当 QO 需要统计估计匹配行数时这是可能的,但在这种特殊情况下,columnsEvents
之间存在外键约束,并且if也存在。然后,我希望优化器假设,对于外部输入中的每一行,内部输入中恰好存在一行,嵌套循环的估计行数也应为 77191 行。为什么这里没有发生这种情况?Location
(LocationId, TenantID)
TenantID
LocationID
NOT NULL
Events
我也很好奇为什么在这种情况下选择嵌套循环以及为什么在排序时会发生溢出。
Pau*_*ite 12
SQL Server 不对执行计划中估计的一致性做出任何一般保证。这些估计确实一开始是一致的,因为基数估计是在早期对整个原始树执行的。此后,可以在不同时间以不同方式重新估计不同的子树(可见计划的部分)。
您可能知道,基于成本的优化器可以探索树的各个部分的逻辑等效表示。当查询树的某些部分生成新的逻辑等价时,优化器可能需要导出新的基数估计和其他本地属性来计算新替代方案的成本。请记住,逻辑替代方案可能具有与原始树片段完全不同的运算符,或者更多或更少。
由于此类推导本质上是统计性的并且对替换子树形状敏感,因此新的估计和属性可能与原始估计和属性有很大不同。即使可以直接选择,也没有通用的方法可以知道更喜欢哪一个。
最终,优化器根据其单独考虑的树的每个部分的成本模型选择最便宜的选项。最终的计划是把每组最便宜的选择“拼凑在一起”。基数估计和其他接缝属性很容易出现不匹配。
查看 Plan Explorer 中的估计行数:
您可以看到 25 行和 41 行估计很受欢迎。这有力地表明了这些数字出现在第一轮基数估计中。
重点关注感兴趣领域的估计:
更大的估计表明对新候选子树的重新估计,具有不同的形状和可能不同的谓词。这只是具有探索功能的基于成本的优化器的正常操作。
考虑到它们所得出的背景,两组估计在逻辑上都是合理且站得住脚的。它们彼此不同意,但这就是事物的本质。事实证明,一种估计比另一种更接近现实。诀窍是提前知道哪个估计更好。
顺便说一句,41 行的初始估计值较低,导致优化器更喜欢在计划的该区域中使用嵌套循环(或者更确切地说是apply )物理实现。它还最终导致排序溢出,因为内存的分配与基数和平均行大小成比例。
节点 22 处的索引查找受到优化位图过滤器的影响,从节点 8 处散列连接的探测端向下推送。此外,它还应用了优化IN ROW
,这意味着它被直接向下推送到存储引擎中并通过以下方式进行评估在任何符合条件的行呈现给查询处理器之前的访问方法。
这是一个优化的位图过滤器,因此在查询优化期间它是存在的,并且考虑了它的影响。这与优化完成后启发式引入的静态位图过滤器不同。因此,位图的静态类型不会影响基数估计。
然而,它比这更复杂。一个IN ROW
图有两个组成部分:一个能够在存储引擎中进行评估的小位图,以及由查询处理器作为残差应用的完整位图。这种差异仅体现在执行后的“实际”行计数效果(例如读取的行数)中,但为了完整性我提到它。
位图过滤器的效果通常在散列连接的探测端的交换运算符(重新分区流)处进行评估。这种替代方案的成本有助于优化器决定是否对位图使用散列连接。
作为单独的优化,后续处理尝试将该过滤器尽可能地推到树中距交换器较远的位置。后面的处理非常基本,不会尝试重新推导相关操作员的成本或估计。它属于被认为“总是好的”事物类别。
在这个计划中,哈希连接的探针端恰好没有交换,因为优化器最终选择使用广播分区来进行节点 10 处的交换。所有行都发送到所有线程,因此不需要探针端分区。然而,位图过滤器被从交换本来的位置向下推。
优化器不是从哈希连接+位图计划开始的。仅在基于成本的探索的并行运行期间才将其视为替代方案。
如果不访问生成此计划的数据库,我无法为您提供包含所有数字和转换详细信息的精确答案。即便如此,根据我的经验,这是一种熟悉的模式:一些估计是早期得出的,并在优化过程中幸存下来。其他的则是在基于成本的优化过程中得出的。最终的弗兰肯计划包括每个方面的各个方面。
你还提到了FK关系。优化器在基数估计期间会考虑这些(尽管并不总是像我们希望的那样完全),但它不会返回并尝试根据这些约束“合理化”最终计划。正如我在一开始所说的,不存在一般的一致性保证。
使用AdventureWorks 示例数据库:
SELECT P.ProductID
FROM Production.Product AS P
JOIN
(
SELECT TH.TransactionID, TH.ProductID
FROM Production.TransactionHistory AS TH
UNION ALL
SELECT THA.TransactionID, THA.ProductID
FROM Production.TransactionHistoryArchive AS THA
) AS U
ON U.ProductID = P.ProductID
OPTION (FORCE ORDER);
Run Code Online (Sandbox Code Playgroud)
我使用FORCE ORDER
提示来防止多次优化器探索,因此我们最终得到的计划形状接近查询的原始书面形式,具有初始基数估计:
142,412 行的最终估计是基于两个表并集上的连接。
删除FORCE ORDER
提示允许优化器考虑多种替代方案。我的例子中选择的是:
这是考虑两个连接的并集而不是并集上的原始连接的结果。有两个新连接需要新的估计。新连接产生113,443和89,253行。
这些输入到一个串联中,该串联保留了 142,412 行的原始估计值,这不再是其输入的总和。不一致。
我们可以将串联重新估计为 113,443 + 89,253 = 202,696 行,但通常无法知道哪种估计更好:从并集上的联接派生的估计值,还是从两个单独联接上的并集派生的估计值。
优化器可以选择对较少派生的估计有更高的置信度,或者对基于已知事实(如可信外键)的估计有更高的置信度。一些内部更改已经在这个总体方向上实现,但这是一个根本性的难题,在 SQL Server 中尚未完全解决或实现。
这个例子非常简单。现实世界的计划通常会在总体最终计划的不同小区域中进行许多独立(且递归!)的探索和重新估计。
有关 SQL Server 优化器操作的概述,请参阅我的优化器深入探讨系列。
归档时间: |
|
查看次数: |
620 次 |
最近记录: |