【问题标题】:Question on how to read a SQL Execution plan关于如何阅读 SQL 执行计划的问题
【发布时间】:2010-01-21 00:56:14
【问题描述】:

我已执行查询并包含实际执行计划。有一个我感兴趣的哈希匹配,因为它的子树使用索引扫描而不是索引搜索。当我将鼠标悬停在这个哈希匹配上时,有一个名为“Probe Residual”的部分。我曾假设这是我加入的任何价值观。我在这里是对的还是对这意味着什么有更好的解释?

我的第二个问题是关于它使用的索引。在我的示例中,我很确定这个特定的连接是在两列上连接的。它正在扫描的索引中包含这两个列以及连接中未使用的另一列。我的印象是,这将导致 Index Seek 而不是 Scan。我是不是搞错了?

【问题讨论】:

    标签: sql sql-server sql-server-2005 sql-execution-plan


    【解决方案1】:

    哈希连接通常(总是?)使用扫描或至少是范围扫描。散列连接的工作原理是扫描左连接表和右连接表(或表中的范围)并构建一个内存中的哈希表,其中包含扫描“看到”的所有值。

    在您的情况下发生的情况是这样的:QO 注意到它可以从恰好包含该列(作为键或作为包含列)的非聚集索引中获取列 C 的所有值。作为一个非聚集索引可能相当狭窄,因此扫描整个非聚集索引的 IO 总量并不夸张。 QO 还认为系统有足够的 RAM 在内存中存储哈希表。当将此查询的成本(例如,10000 页的非聚集索引端到端扫描)与使用搜索的嵌套循环的成本(例如 5000 次探测,每次 2-3 页)进行比较时,扫描因需要较少的 IO 而获胜。当然,这在很大程度上是我的猜测,但我试图从 QO 的角度来介绍这个案例,并且该计划可能是最优的。

    促成这一特定计划选择的因素是:

    • 连接右侧有大量估计的候选者
    • 左侧窄非聚集索引中连接列的可用性
    • 大量内存

    对于候选数量的大量估计,比散列连接更好的选择只是合并连接,并且需要对输入进行预排序。如果左侧都可以提供保证连接列上的顺序的访问路径,并且右侧具有类似的可能性,那么您最终可能会使用合并连接,这是最快的连接。

    【讨论】:

    • 哈希匹配不一定使用扫描。它可以很容易地涉及对特定记录的搜索,然后在哈希匹配中使用该搜索的结果。对于嵌套循环,它一次处理一条记录,因此更有可能更喜欢 Seek,但这并不意味着 Hash 更喜欢扫描 - 它只需要获取所有可能匹配的行。如果您要过滤所涉及的两个表,并且有一个覆盖索引和一个计算,则可以重现此行为。
    • @Rob:我对此不以为然。我花了一段时间才找到一个公开可用的 ref,但请阅读 blogs.msdn.com/craigfr/archive/2006/08/10/687630.aspx 了解 Hash-Join 的工作原理,构建和探测阶段 一次性读取整个输入寻求。伪算法也清楚地表明左右两侧之间没有相关性来决定探测过滤。
    • 对...让我们先考虑设置。创建两个表,每个表有两个字段。过滤字段上的索引一,包括连接字段列。接下来,我们将用数字填充它们。创建表 dbo.table1 (id int identity(1,1) primary key ,joinfield int ,filterfield int ); go create table dbo.table2 (id int identity(1,1) primary key ,joinfield int ,filterfield int );在 dbo.table1(filterfield) include (joinfield) 上创建索引 ix1;在 dbo.table2(filterfield) include (joinfield) 上创建索引 ix2;去
    • 这里是人口脚本。 insert dbo.table1 (joinfield, filterfield) select number, number from master..spt_values where type = 'P'; insert dbo.table2 (joinfield, filterfield) select number, number from master..spt_values where type = 'P'; go /* 现在是关键部分 我加入了 joinfield,但我先过滤。即使连接使用散列,过滤器也会导致查找。如果不是,请使用 INNER HASH JOIN */ select * from dbo.table1 t1 inner join dbo.table2 t2 on t1.joinfield = t2.joinfield where t1.filterfield between 100 and 200 and t2.filterfield between 100 and 200 ;
    • @Rob:但这些都是范围寻求访问,无论是在散列运算符的左侧和右侧子代。我的观点是哈希连接不会与精确(键匹配)搜索一起使用,它总是将输入作为扫描(范围扫描,即搜索后扫描)。
    【解决方案2】:

    This blog post will probably answer your first question.

    至于您的第二个,优化器可能会在多种情况下选择索引扫描。在我脑海中浮现:

    • 如果索引非常小
    • 如果索引中的大部分行都会被查询选中

    • 如果您在查询的 where 子句中使用函数

    对于前两种情况,执行扫描更有效,因此优化器选择它而不是搜索。对于第三种情况,优化器别无选择。

    【讨论】:

    • 非常好的文章,感谢您发布它。那么他是说如果我的查询没有加入索引的第一列,那么它可能会导致索引扫描而不是搜索?
    • 是的。顺便说一句,他的博客非常适合学习 sql server 的内部工作原理。
    • 是的,我在那里读到的东西给我留下了深刻的印象。将他添加到我的列表中。谢谢你把我指向他!
    【解决方案3】:

    1/ 哈希匹配意味着它需要在相等连接中使用的列的哈希,但需要包括连接中涉及的所有其他列(对于 > 等),以便它们也可以被检查。这就是残差列的用武之地。

    2/ 如果可以直接找到所需的行,则可以进行索引查找。也许您正在对列应用计算并使用它?然后它将使用索引作为数据的较小版本,但仍需要检查每一行(对每一行应用计算)。

    【讨论】:

      【解决方案4】:

      在 simple-talk.com 上查看那些关于执行计划的优秀文章:

      他们还有免费的电子书SQL Server execution plans 供下载。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-28
        • 2017-11-22
        • 1970-01-01
        • 1970-01-01
        • 2011-10-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多