Ola Hallengren 索引维护 - 命令之间的时间长吗?

Eri*_*ing 7 sql-server maintenance ola-hallengren

我在所有服务器上运行 Ola Hallengren 脚本以进行索引和统计维护。当我查看命令日志表时,我注意到命令结束和下一个命令开始之间的时间很长。有时这个间隔会超过一个小时。

有没有其他人在他们的系统上观察到这一点?我可以做些什么来缩短(我猜)要维护的项目之间的发现时间?下面是我运行它们的参数集。

sqlcmd -E -S $(ESCAPE_SQUOTE(SRVR)) -d master -Q "EXECUTE [dbo].[IndexOptimize] 
@Databases = 'USER_DATABASES', 
@LogToTable = 'Y',
@FragmentationLow = NULL,
@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationLevel1 = 50,
@FragmentationLevel2 = 80,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y' " -b
Run Code Online (Sandbox Code Playgroud)

所以当我运行这个时:

SELECT DATEDIFF(MINUTE, cl.StartTime, cl.EndTime)
, *
FROM master.dbo.CommandLog AS cl
WHERE cl.StartTime > '2014-12-13'
ORDER BY cl.ID
Run Code Online (Sandbox Code Playgroud)

我看到这个:

样本输出

Eri*_*ing 5

深入研究一下,使用通用查询检索索引碎片只需大约一个小时,即使在 1.5TB 数据库上返回 160 行:

SELECT  s.name AS schemaname ,
        t.name AS tablename ,
        t.object_id ,
        i.name AS indexname ,
        i.index_id ,
        x.page_count ,
        x.avg_fragmentation_in_percent ,
        x.avg_page_space_used_in_percent ,
        i.type_desc
FROM    sys.dm_db_index_physical_stats(DB_ID(
                                ), NULL, NULL, NULL, 'LIMITED') x
        INNER JOIN sys.tables t ON x.object_id = t.object_id
        INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
        INNER JOIN sys.indexes i ON x.object_id = i.object_id
                                AND x.index_id = i.index_id
WHERE   x.index_id > 0
        AND alloc_unit_type_desc = 'IN_ROW_DATA'
        AND x.page_count > 1000;
Run Code Online (Sandbox Code Playgroud)

这让我相信,当索引维护例程的这部分运行时,在新数据库开始时收集初始信息和构建命令需要很长时间。

SET @CurrentCommand12 = @CurrentCommand12 + 
'SELECT @ParamFragmentationLevel = MAX(avg_fragmentation_in_percent), 
@ParamPageCount = SUM(page_count) 
FROM sys.dm_db_index_physical_stats(
@ParamDatabaseID, @ParamObjectID, @ParamIndexID,     @ParamPartitionNumber, ''LIMITED'')     
WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND     index_level = 0'

EXECUTE sp_executesql @statement = @CurrentCommand12, @params = N'@ParamDatabaseID int, 
@ParamObjectID int, @ParamIndexID int, @ParamPartitionNumber int, 
@ParamFragmentationLevel float OUTPUT, @ParamPageCount bigint OUTPUT', 
@ParamDatabaseID= @CurrentDatabaseID, @ParamObjectID = @CurrentObjectID, @ParamIndexID = 
@CurrentIndexID, @ParamPartitionNumber = @CurrentPartitionNumber, 
@ParamFragmentationLevel = @CurrentFragmentationLevel OUTPUT, @ParamPageCount = 
@CurrentPageCount OUTPUT
Run Code Online (Sandbox Code Playgroud)


Joh*_* N. 3

根本原因是 DMF与扫描模式sys.dm_db_index_physical_stats的结合。

(我强调原始描述的有限部分)

执行函数的模式决定了为获取函数所使用的统计数据而执行的扫描级别。模式指定为 LIMITED、SAMPLED 或 DETAILED。该函数遍历页链以查找构成表或索引的指定分区的分配单元。sys.dm_db_index_physical_stats 仅需要意向共享 (IS) 表锁,无论其运行模式如何。

LIMITED 模式是最快的模式,扫描的页面数最少。对于索引,仅扫描 B 树的父级页面(即叶级以上的页面)。对于堆,将检查关联的 PFS 和 IAM 页,并在 LIMITED 模式下扫描堆的数据页。

在 LIMITED 模式下,compressed_pa​​ge_count 为 NULL,因为数据库引擎仅扫描 B 树的非叶页以及堆的 IAM 和 PFS 页。使用SAMPLED 模式获取compressed_pa​​ge_count 的估计值,并使用DETAILED 模式获取compressed_pa​​ge_count 的实际值。SAMPLED 模式返回基于索引或堆中所有页面的 1% 样本的统计信息。SAMPLED 模式下的结果应被视为近似值。如果索引或堆的页数少于 10,000,则使用 DETAILED 模式而不是 SAMPLED。

DETAILED 模式扫描所有页面并返回所有统计信息。

从“有限”到“详细”,这些模式的速度逐渐变慢,因为每种模式中都会执行更多的工作。要快速测量表或索引的大小或碎片级别,请使用 LIMITED 模式。它是最快的,并且不会为索引的 IN_ROW_DATA 分配单元中的每个非叶级别返回一行。

参考: sys.dm_db_index_physical_stats (Transact-SQL) | 扫描模式(Microsoft 文档)

即使 Ola 的脚本正在执行sys.dm_db_index_physical_statsinLIMITED模式,根据数据量的不同,扫描非常大的堆也可能需要很长时间。因为您正在使用,所以@UpdateStatistics = 'ALL'您告诉脚本更新所有统计信息(INDEXCOLUMN),其中包括堆列的统计信息。

可能的解决方案

您可能需要考虑不更新所有对象的统计信息,而是将范围限制为INDEX或然后考虑更改以下参数:

@OnlyModifiedStatistics = 'Y'
Run Code Online (Sandbox Code Playgroud)

默认是N

仅当自最近统计信息更新以来有任何行被修改时才更新统计信息。

参考: SQL Server索引和统计维护(ola.hallengren.com)