我正在针对 SQL Server 2019 CU14 进行测试。我有一个纯行模式查询,可以从复杂视图中选择前 50 行。完整的查询在 MAXDOP 1 处需要 25426 毫秒的 CPU 时间,在 MAXDOP 2 处需要 19068 毫秒的 CPU 时间。并行查询总体上使用更少的 CPU 时间并不感到惊讶。并行查询适用于位图运算符,并且查询计划在一些方面有所不同。但是,我对前 N 个排序的操作员时间的巨大差异感到惊讶。
在串行计划中,根据算子执行统计,前 N 个排序报告了大约 10 秒的 CPU 时间:
MAXDOP 2 计划为相同的前 N 排序报告大约 1.6 秒的 CPU 时间:
我不明白为什么两个不同的查询计划之间会报告如此大的差异。父运算符中的计算标量非常简单,无法解释运算符时间的差异。这是他们的样子:
[Expr1055] = Scalar Operator(CASE WHEN COLUMN_1 IS NULL THEN (0) ELSE datediff(day,COLUMN_1,getdate()) END),
[Expr1074] = Scalar Operator(CASE WHEN [Expr1074] IS NULL THEN (0) ELSE [Expr1074] END)
在计划的不同部分还有其他计算标量。如果有人想查看它们,我上传了串行计划和并行计划的匿名实际计划。
当我将没有 TOP 的完整查询结果加载到临时表中并在临时表上执行 TOP 50 排序时,并行和串行计划都需要大约 1200 毫秒的 CPU 时间来执行排序。因此,在完整查询中报告的并行排序操作员时间对我来说是合理的。串行查询的十秒没有。
为什么串行前 N 排序报告的 CPU 时间比并行排序高得多?它的效率真的低很多吗?或者这可能是操作执行统计的错误?
很难根据没有重现的匿名计划来确定,但首先想到的机制是延迟表达式评估。
在串行计划中,排序可能负责评估大量从早期运算符延迟的表达式。这包括必须在排序缓冲区中具体化的值,而不仅仅是排序键。因此,计算表达式的成本由排序承担。
在并行计划中,可能必须更早地评估这些表达式,例如,当值必须在Parallelism运算符的交换缓冲区中实现时。表达式评估的成本分散在计划运算符中,而不是集中在Sort中。
在Sort中有大量的表达式(Exprxxxx)。下面显示了一个选择(它们不会都适合单个屏幕截图):
我没有试图追踪其中哪些可能是负责任的,因为该计划的规模和匿名性质使得这不可行。
有许多运算符可以定义表达式,而不仅仅是Compute Scalar。引用我之前链接的文章:
表达式评估可以跨许多运算符延迟很长时间。关键点是评估被推迟到另一个运算符需要表达式的结果,或者甚至直到服务器需要组装行以返回给客户端。阻塞或半阻塞运算符只是可能需要实现表达式评估的一个示例。
其他语言的用户可能熟悉名为“惰性评估”的概念。计算在逻辑上尽可能推迟。
自然地,将结果具体化到临时表中意味着对所有表达式进行求值。这解释了为什么您会看到从临时表源执行的预期 CPU 时间。