【问题标题】:Why CTE (recursive) is not parallelized (MAXDOP=8)?为什么 CTE(递归)没有并行化(MAXDOP=8)?
【发布时间】:2011-07-18 07:52:06
【问题描述】:

我们有相当大的机器 100GB+ 内存和 8+ 个内核。服务器范围的 MAXDOP=8。

T_SEQ_FF rowcount = 61692209, size = 2991152 KB  

UPD 1:T_SEQ_FF 有两个索引:

1) create index idx_1 on T_SEQ_FF (first_num)
2) create index idx_2 on T_SEQ_FF (second_num)

T_SEQ_FFfirst_numsecond_num pairs 的nums 应该在cte 之后提供一个序列:

;with first_entity as ( 
    select first_num from  T_SEQ_FF a  where not exists (select 1 from  T_SEQ_FF b  where a.first_num = b.second_num) 
) ,
cte as ( 
select a.first_num, a.second_num, a.first_num as first_key, 1 as sequence_count 
from  T_SEQ_FF a  inner join first_entity b on a.first_num = b.first_num 
union all 
select a.first_num, a.second_num, cte.first_key, cte.sequence_count + 1 
from  T_SEQ_FF a  
inner join cte on a.first_num = cte.second_num 
) 
select * 
from cte 
option (maxrecursion 0); 

但是当我运行这个查询时 - 我只看到没有并行的串行查询计划。 如果我从上面的查询中删除 CTE 的第二部分:

union all 
    select a.first_num, a.second_num, cte.first_key, cte.sequence_count + 1 
    from  T_SEQ_FF a  
    inner join cte on a.first_num = cte.second_num 

然后我可以看到查询计划使用 Repartition 和 Gather Streams 变得并行化

所以我可以总结一下,这是因为 recurisve CTE SQL Server 在处理这个查询时没有使用并行。

我相信在拥有大量免费资源的大型机器上,并行性应该有助于更快地完成查询。

目前它运行约 40-50 分钟。

您能否建议我们如何使用尽可能多的资源来更快地完成查询?

CTE 是唯一的选择,因为我们需要从 first_num - second_num 对中填充序列,而这些序列可以是任意长度。

【问题讨论】:

  • 你有关于 T_SEQ_FF.second_num 的索引吗?
  • 是的,我已将我们使用的索引创建子句添加到主题中。
  • 我猜这是 recursive 部分,而不是 CTE。
  • 是的,正是我的错误——我称 CTE 为“递归 CTE”。那么为什么 SQL Server 至少不能并行化 SCAN 表(索引)以进行递归部分的准备步骤?为什么整个查询是串行的。
  • @zmische - 我猜这不是答案,但我认为这是因为第二个查询取决于第一个。由于第二个内部加入了第一个,它们不会同时运行。从效率的角度来看,限制INNER JOIN 返回的行比同时运行两者然后过滤掉无效行更有意义。

标签: sql sql-server performance sql-server-2008


【解决方案1】:

我会尝试重写 CTE 以删除其中一个步骤,即

;cte as ( 
select a.first_num, a.second_num, a.first_num as first_key, 1 as sequence_count 
from  T_SEQ_FF a  where not exists (select 1 from  T_SEQ_FF b  where a.first_num = b.second_num) 
union all 
select a.first_num, a.second_num, cte.first_key, cte.sequence_count + 1 
from  T_SEQ_FF a  
inner join cte on a.first_num = cte.second_num 
) 
select * 
from cte 
option (maxrecursion 0);

如果只有一个根元素,最好将其作为变量传递到查询中,以便查询优化器可以使用该值。

要尝试的另一件事是更改查询以获取没有子查询的根元素,即 second_num 为 null 或 first_num = second_num。

【讨论】:

  • 我刚刚尝试了您的查询 - 结果与以前相同 - 串行计划。我们保存了一个 HASH Join 虽然这可能会有所帮助。
【解决方案2】:

我不确定这是否是一个可行的选择,但我们已经排除了许多其他传统方法:您能否通过将 first_entity 集拆分为多个片段,然后通过代码并行运行此查询来进行显式并行化,以及最后将这些数据集合并在一起。

这比 t-sql 解决方案要复杂得多,而且我不知道这是否适用于您的数据,数据分布和锁定都可能是这里的问题。

【讨论】:

    【解决方案3】:

    我偶然发现了一个类似的问题,在仔细分析了情况以及UNION ALL Performance IN SQL Server 2005 中的问题后,在我看来,在 UNION ALL 查询中引用 cte 会关闭并行化(很可能是一个错误) .

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-21
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多