我记得我曾经在一张表上丢失了一个索引,并且一个查询需要很长时间。在执行计划中,我看到完成了表扫描和合并。来自 IO 的统计数据显示每个核心都进行了一次表扫描,或者更好的是我进行了 6 次表扫描。现在我想知道是每个核心都进行全扫描,还是每个核心都粗略地进行 1/6 表扫描?我确信如果我有一个合适的索引,我要么只进行一次查找,要么将它拆分为每个核心。
我希望你能理解我在这里的意思,不幸的是我无法提供任何信息,因为这个问题刚刚出现,我的问题在几年前就已经解决了。
我记得我曾经在一张表上丢失了一个索引,并且一个查询需要很长时间。在执行计划中,我看到完成了表扫描和合并。来自 IO 的统计数据显示每个核心都进行了一次表扫描,或者更好的是我进行了 6 次表扫描。现在我想知道是每个核心都进行全扫描,还是每个核心都粗略地进行 1/6 表扫描?我确信如果我有一个合适的索引,我要么只进行一次查找,要么将它拆分为每个核心。
我希望你能理解我在这里的意思,不幸的是我无法提供任何信息,因为这个问题刚刚出现,我的问题在几年前就已经解决了。
坦率地说,我不太确定“合并已完成”是什么意思。你在谈论合并连接吗?也许你的意思是并行运算符?至少我可以回答有关并行表扫描的问题。
我假设你的意思是你
SET STATISTICS IO ON
在运行查询之前运行并且部分输出包含如下内容:“扫描计数”标签有点误导。如果报告 6 次扫描,您不应该得出结论,即
STATISTICS IO
表中的所有行都被扫描了 6 次。考虑以下对名为 的堆表的简单示例查询heap_table
:对于该查询,
STATISTICS IO
应该报告扫描计数为 1,对吗?但是 SQL Server 显然不需要读取表中的所有行。查看扫描计数标签的定义也很有帮助:因此,如果您的查询进行了并行扫描,我希望看到的扫描计数至少为 6,但这并不一定意味着表中的所有行都被读取了六次。你怎么知道这些行是如何分布在你的 CPU 核心之间的?
最简单的方法是只看一个实际的执行计划。如果您查看并行扫描的详细信息,SQL Server 将显示每个 CPU 线程处理了多少行。下面是一张您可能会从 Paul White 的文章并行执行计划 – 分支和线程中看到的图片:
正如您所说,您多年前遇到过此查询,因此该方法对您没有帮助。相反,我们需要查看 SQL Server 可用于并行计划处理的技术。Craig Freedman 有一系列关于这个主题的博客文章。来自并行扫描文章:
那么,你有它。正如我之前所说,您可以通过使用并行扫描运行查询并检查实际执行计划中并行扫描运算符的详细信息来轻松测试它。
从另一种角度来看,请尝试考虑一个场景,在该场景中,SQL Server 对每个核心进行全表扫描是有益的。
假设您碰巧有一个
UNION ALL
查询引用了您的表六次。原则上,SQL Server 可以独立地用一个内核进行每个表扫描,最后合并结果。但是,SQL Server 不会这样做,因为它不会进行管道并行处理。即使可以,我个人也想不出在这里这样做有什么好处,除了避免一些与并行相关的开销。您可能会阅读广播类型的并行执行,并想知道在那种情况下 SQL Server 是否可以对表进行六次完整扫描,每个内核一次。对于广播类型的交换,SQL Server 将所有行发送到所有消费者线程。然而,这可以通过对表进行串行扫描,然后进行分布式流类型的并行交换来实现。事实上,这就是您在散列连接示例中看到的。我真的想不出并行扫描有什么好处,尤其是当广播类型只用于相对较小的表时。
我认为可能会发生这种情况的一种情况是,如果您有一个并行嵌套循环连接,其中包含一个包含 6 行的外部表,并且在连接的内侧进行了表扫描。在那种情况下,我相信表扫描将由独立的串行线程完成,因此您可以有效地让每个核心执行自己的表扫描。当然,这样的查询可能执行得非常差并且不是目标,尤其是当外表超过六行时。