Mar*_*ith 11 sql-server parallelism execution-plan sql-server-2017
DROP TABLE IF EXISTS #EmptyTable, #BigTable
CREATE TABLE #EmptyTable(A int);
CREATE TABLE #BigTable(A int);
INSERT INTO #BigTable
SELECT TOP 10000000 CRYPT_GEN_RANDOM(3)
FROM sys.all_objects o1,
sys.all_objects o2,
sys.all_objects o3;
Run Code Online (Sandbox Code Playgroud)
WITH agg
AS (SELECT DISTINCT a
FROM #BigTable)
SELECT *
FROM #EmptyTable E
INNER HASH JOIN agg B
ON B.A = E.A;
Run Code Online (Sandbox Code Playgroud)
这是我今天之前没有注意到的现象的简化再现。我对内部散列连接的期望是,如果构建输入为空,则不应执行探测端,因为连接不会返回任何行。上面的示例与此相反,并从表中读取了 1000 万行。这使查询的执行时间增加了 2.196 秒 (99.9%)。
OPTION (MAXDOP 1)
执行计划从#BigTable
. 该ActualExecutions
是0
对哈希连接内所有的运营商。SELECT * FROM #EmptyTable E INNER HASH JOIN #BigTable B ON B.A = E.A
- 我得到一个并行计划,散列连接内部的扫描运算符确实具有ActualExecutions
DOP,但仍然没有读取任何行。该计划没有重新分区流操作符(或聚合)这里发生了什么?为什么原计划会出现问题而其他情况没有?
Pau*_*ite 10
当构建为空时不运行连接的探测端是一种优化。当探测端有一个子分支时,即当有一个交换操作符时,它不适用于并行行模式散列连接。
多年前,Adam Machanic 在现已不存在的 Connect 反馈网站上有一份类似的报告。场景是探针端的一个启动过滤器,它意外地运行了它的子操作符。微软的回答是引擎需要保证某些结构被初始化,唯一合理的强制执行方法是确保探针端操作符是打开的。
我自己对细节的回忆是,不初始化子树会导致难以修复的并行计时错误。确保子分支启动是解决这些问题的方法。
批处理模式散列连接没有这种副作用,因为管理线程的方式不同。
在您的特定情况下,效果更明显,因为哈希聚合正在阻塞;它在迭代器的 Open() 调用期间消耗其全部输入。当探测端只有流操作符时,性能影响通常会更加有限,这取决于将第一行返回到散列连接的探测端所需的工作量。
归档时间: |
|
查看次数: |
702 次 |
最近记录: |