急切的后台打印操作符对于从群集的列存储中进行此删除有用吗?


28

我正在测试从群集的列存储索引中删除数据。

我注意到执行计划中有一个急切的假脱机操作员:

在此处输入图片说明

具有以下特征:

  • 删除6000万行
  • 1.9使用GiB TempDB
  • 14分钟执行时间
  • 连续计划
  • 1在线轴上重新绑定
  • 估计扫描成本:364.821

如果我诱使估算器低估了,我会得到一个更快的计划,避免使用TempDB:

在此处输入图片说明

估计扫描成本:56.901

(这是一个估计的计划,但是注释中的数字正确。)

有趣的是,如果我通过运行以下命令刷新增量存储,则线轴会再次消失:

ALTER INDEX IX_Clustered ON Fact.RecordedMetricsDetail REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);

仅当增量存储中的页面阈值超过某个阈值时才引入假脱机。

要检查增量存储的大小,我正在运行以下查询来检查表的行内页面:

SELECT  
  SUM([in_row_used_page_count]) AS in_row_used_pages,
  SUM(in_row_data_page_count) AS in_row_data_pages
FROM sys.[dm_db_partition_stats] as pstats
JOIN sys.partitions AS p
ON pstats.partition_id = p.partition_id
WHERE p.[object_id] = OBJECT_ID('Fact.RecordedMetricsDetail');

第一个计划中的假脱机迭代器是否有任何合理的好处?我必须假定它旨在提高性能,而不是用于万圣节保护,因为它的存在不一致。

我正在2016 CTP 3.1上对此进行测试,但在2014 SP1 CU3上却看到了相同的行为。

我已经发布了一个脚本,该脚本可以生成模式和数据,并引导您在此处演示问题。

目前,这个问题主要是出于对优化程序行为的好奇,因为我有一个解决该问题的方法,该问题促使了这个问题(一个大的线轴填充了TempDB)。我现在通过使用分区切换来删除。


2
如果我尝试OPTION (QUERYRULEOFF EnforceHPandAccCard),线轴将消失。我认为惠普可能是“万圣节保护”。但是,然后尝试使用带有USE PLAN提示的计划失败了(就像尝试通过OPTIMIZE FOR 变通办法使用该计划一样)
Martin Smith

谢谢@MartinSmith。知道AccCard会是什么吗?升序基数基数也许?
James L

1
@JamesLupolt不,我无法提出任何特别令人信服的东西。也许累积积累或访问?
马丁·史密斯

Answers:


22

第一个计划中的假脱机迭代器是否有任何合理的好处?

这取决于您认为“合理”的条件,但是根据成本模型得出的答案是肯定的。当然这是正确的,因为优化器总是选择找到的最便宜的计划。

真正的问题是,为什么成本模型认为带线轴的计划比不带线轴的计划便宜得多。在将任何行添加到增量存储之前,请考虑为新表(通过脚本)创建的估计计划:

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);

该计划的估计成本为771,734单位的巨额资金

原计划

成本几乎全部与聚集索引删除相关,因为删除预计会导致大量的随机I / O。这只是适用于所有数据修改的通用逻辑。例如,假设对b树索引的无序修改集会导致大量的随机I / O,并伴有较高的I / O成本。

正是出于这些成本原因,数据更改计划可能具有“排序”功能,以便按顺序显示行,从而促进顺序访问。在这种情况下,由于表已分区,因此影响更加严重。实际上,它非常分区。您的脚本创建了15,000个脚本。由于对中游切换分区(行集)的代价也很高,因此对一个非常分区的表进行随机更新的代价特别高。

最后要考虑的主要因素是,上面的简单更新查询(其中“更新”表示任何数据更改操作,包括删除)均符合称为“行集共享”的优化条件,其中,相同的内部行集用于扫描和更新表。执行计划仍显示两个单独的运算符,但是,仅使用了一个行集。

我之所以这么说是因为能够应用此优化意味着优化器采用的代码路径根本没有考虑显式排序的潜在好处,从而降低了随机I / O的成本。在表是b树的情况下,这是有道理的,因为结构固有地是有序的,因此共享行集会自动提供所有潜在的好处。

重要的结果是,更新运算符的成本核算逻辑不会考虑基础对象为列存储的这种排序优势(促进顺序I / O或其他优化)。这是因为列存储修改未就地执行;他们使用增量存储。因此,成本模型反映了b树上的共享行更新与列存储之间的差异。

但是,在(非常!)分区列存储的特殊情况下,保留顺序可能仍然有好处,因为从I / O角度来看,在移至下一个分区之前对一个分区执行所有更新可能仍然是有利的。 。

标准的成本逻辑可在此处重新用于列存储,因此保留分区顺序(尽管不是每个分区内的顺序)的计划的成本较低。通过使用未记录的跟踪标志2332要求对更新操作符进行排序的输入,我们可以在测试查询上看到这一点。这会将DMLRequestSort属性在更新时设置为true,并迫使优化器生成一个计划,该计划为一个分区提供所有行,然后再移至下一个分区:

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);

该计划的估计成本要低得多,为52.5174个单位:

DMLRequestSort =真实计划

成本的降低全部归因于更新时估算的I / O成本较低。引入的后台打印程序不执行任何有用的功能,只是它可以保证按更新的要求按分区顺序输出DMLRequestSort = true(列存储索引的串行扫描无法提供此保证)。线轴本身的成本被认为是相对较低的,尤其是与更新时的成本降低(可能不切实际)相比。

在查询优化中非常早就做出了是否需要对更新操作符进行有序输入的决定。该决策中使用的启发式方法从未被记录下来,但是可以通过反复试验来确定。似乎任何增量存储的大小都是此决定的输入。做出选择后,该选择对于查询编译是永久的。没有任何USE PLAN提示会成功:计划的目标或者已经命令了更新的输入,或者没有。

还有一种方法可以为该查询获取低成本计划,而无需人为地限制基数估计。避免假脱机处理的足够低的估计值可能会导致DMLRequestSort为假,由于预期的随机I / O导致非常高的估计计划成本。另一种选择是将跟踪标志8649(并行计划)与2332(DMLRequestSort = true)结合使用:

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);

这将导致一个计划,该计划使用按分区的批处理模式并行扫描和顺序保留(合并)的Gather Streams交换:

有序删除

根据您的硬件上分区排序的运行时有效性,这可能会在三种方法中表现最好。也就是说,对列存储进行大的修改并不是一个好主意,因此分区切换的主意几乎可以肯定更好。如果您可以应对分区对象经常出现的长编译时间和古怪的计划选择,尤其是在分区数量很大的情况下。

结合许多相对较新的功能,尤其是接近其极限,是获得不良执行计划的好方法。优化程序支持的深度会随着时间的推移而不断提高,但是使用15,000个列存储分区可能总是意味着您生活在有趣的时期。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.