REORGANIZE 似乎一直和 REBUILD 一样有效

Vac*_*ano 7 index sql-server sql-server-2012

在这个MSDN 页面上,它说您是否应该根据碎片量重新组织或重建:

 5% 到 30% -> 更改索引重组  
 超过 30% -> ALTER INDEX REBUILD WITH (ONLINE = ON)*

但是,我们注意到,即使大小表上的碎片非常高(超过 95%),REORGANIZE 也能正常工作。(碎片下降到不到 1%)。

为什么 MSDN 页面会这样说?它不应该像我一样工作吗?

或者我缺少什么缺点?(如果我不重建,隐藏的问题?)

Aar*_*and 9

让我引用同一页的另一部分,基本上说它取决于- 强调我的:

这些值提供了一个粗略的指导方针,用于确定您应该在 ALTER INDEX REORGANIZE 和 ALTER INDEX REBUILD 之间切换的点。但是,实际值可能因情况而异。通过试验来确定适合您的环境的最佳阈值是很重要的。

除了“这取决于”之外,对此没有很好的答案......这些只是一般准则,可能适合也可能不适合,具体取决于行数、索引中的列、它们的数据类型、索引的宽度,在写入这些表之间还有什么被写入磁盘,等等。

就像我应该调整 tempdb 的大小,PLE 的合适门槛是多少,以及我应该购买什么类型的汽车,答案都是一样的:它取决于


Eri*_*ing 7

在我的环境中,最大的不同在于 Reorganize 是单线程的,而 Rebuild 可以与处理器一样并行。重建还具有更新统计数据的额外好处,因此您可以在重建后放弃单独的统计数据更新。

从我的角度来看,处理一些大的(100GB 到多 TB)表时,我几乎将所有内容都设置为重建。我看过 Reorganize 在一个 PK(300GB 表)上花费了 8 个小时并且几乎没有任何影响。我全权负责 8 台服务器上大约 100TB 的数据,而且我们的维护时段不是最长的(凌晨 1 点到早上 7 点)。

我目前使用Ola Hallengren脚本,但我希望在未来几个月内转向Minion Reindex,因为它们提供了预取您想要处理的对象的能力,因此可以花费整个维护窗口维护,而不是收集碎片数据。收集碎片数据函数可以在较大的表上运行很长时间,即使在 LIMITED 模式下。


swa*_*eck 7

@AaronBertrand 有一个很好的答案并且是正确的 -这取决于。我想补充一点,您只关注一个指标 - 索引碎片。这是一个好的开始,但还有其他事情需要考虑。

考虑以下场景(使用 AdventureWorks2014):

select * 
from Sales.SalesOrderDetail
where SalesOrderID < 50000;

--modify
delete 
from Sales.SalesOrderDetail
where SalesOrderID < 50000;
go

select 
    SchemaName = s.name,
    TableName = t.name, 
    IndexName = idx.name, 
    StatsName = st.name, 
    AvgFragmentationPct = idx.avg_fragmentation_in_percent,
    StatsModifications = st.modification_counter,
    StatsPctModified = 100.*st.modification_counter / st.unfiltered_rows,
    StatsSampleRate = st.sample_rate, 
    StatsLastUpdate = st.last_updated,
    Rows = st.rows
from (
    select s.object_id, s.name, sp.modification_counter, sp.unfiltered_rows, s.stats_id, sp.last_updated, sample_rate = (100.*sp.rows_sampled)/sp.unfiltered_rows , sp.rows
    from sys.stats s 
    cross apply sys.dm_db_stats_properties(s.object_id,s.stats_id) sp 
) st
left join (
        select ips.object_id, i.name, ips.partition_number, ips.avg_fragmentation_in_percent, i.index_id
        from sys.dm_db_index_physical_stats(db_id(),null, null, null, default) ips
        join sys.indexes i 
            on ips.index_id = i.index_id
            and ips.object_id = i.object_id
        ) idx
on st.object_id = idx.object_id
    and st.stats_id = idx.index_id
join sys.tables t 
    on st.object_id = t.object_id
join sys.schemas s
    on t.schema_id = s.schema_id
where s.name = 'Sales' and t.name = 'SalesOrderDetail'
go

delete 
from Sales.SalesOrderDetail
where ProductID = 779
go

select 
    SchemaName = s.name,
    TableName = t.name, 
    IndexName = idx.name, 
    StatsName = st.name, 
    AvgFragmentationPct = idx.avg_fragmentation_in_percent,
    StatsModifications = st.modification_counter,
    StatsPctModified = 100.*st.modification_counter / st.unfiltered_rows,
    StatsSampleRate = st.sample_rate, 
    StatsLastUpdate = st.last_updated,
    Rows = st.rows
from (
    select s.object_id, s.name, sp.modification_counter, sp.unfiltered_rows, s.stats_id, sp.last_updated, sample_rate = (100.*sp.rows_sampled)/sp.unfiltered_rows , sp.rows
    from sys.stats s 
    cross apply sys.dm_db_stats_properties(s.object_id,s.stats_id) sp 
) st
left join (
        select ips.object_id, i.name, ips.partition_number, ips.avg_fragmentation_in_percent, i.index_id
        from sys.dm_db_index_physical_stats(db_id(),null, null, null, default) ips
        join sys.indexes i 
            on ips.index_id = i.index_id
            and ips.object_id = i.object_id
        ) idx
on st.object_id = idx.object_id
    and st.stats_id = idx.index_id
join sys.tables t 
    on st.object_id = t.object_id
join sys.schemas s
    on t.schema_id = s.schema_id
where s.name = 'Sales' and t.name = 'SalesOrderDetail'
go
Run Code Online (Sandbox Code Playgroud)

我们_WA_Sys_00000006_44CA3770通过过滤创建一个新的自动创建的统计对象(在本例中为 )SalesOrderID,然后我们删除行(约 30,000 行)。然后我们通过删除具有特定ProductID. 这很重要,因为它是这些索引/统计数据中至少一个的前导列,反过来,这对于选择性计算原因(统计数据)和 b 树遍历(索引)很重要。

在此处输入图片说明

索引碎片还不错,所以让我们重新组织一下。

alter index all on Sales.SalesOrderDetail reorganize;
go

select 
    SchemaName = s.name,
    TableName = t.name, 
    IndexName = idx.name, 
    StatsName = st.name, 
    AvgFragmentationPct = idx.avg_fragmentation_in_percent,
    StatsModifications = st.modification_counter,
    StatsPctModified = 100.*st.modification_counter / st.unfiltered_rows,
    StatsSampleRate = st.sample_rate, 
    StatsLastUpdate = st.last_updated,
    Rows = st.rows
from (
    select s.object_id, s.name, sp.modification_counter, sp.unfiltered_rows, s.stats_id, sp.last_updated, sample_rate = (100.*sp.rows_sampled)/sp.unfiltered_rows , sp.rows
    from sys.stats s 
    cross apply sys.dm_db_stats_properties(s.object_id,s.stats_id) sp 
) st
left join (
        select ips.object_id, i.name, ips.partition_number, ips.avg_fragmentation_in_percent, i.index_id
        from sys.dm_db_index_physical_stats(db_id(),null, null, null, default) ips
        join sys.indexes i 
            on ips.index_id = i.index_id
            and ips.object_id = i.object_id
        ) idx
on st.object_id = idx.object_id
    and st.stats_id = idx.index_id
join sys.tables t 
    on st.object_id = t.object_id
join sys.schemas s
    on t.schema_id = s.schema_id
where s.name = 'Sales' and t.name = 'SalesOrderDetail'
go
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

重组没有带来任何变化。

让我们重建:

alter index all on Sales.SalesOrderDetail rebuild;
go

select 
    SchemaName = s.name,
    TableName = t.name, 
    IndexName = idx.name, 
    StatsName = st.name, 
    AvgFragmentationPct = idx.avg_fragmentation_in_percent,
    StatsModifications = st.modification_counter,
    StatsPctModified = 100.*st.modification_counter / st.unfiltered_rows,
    StatsSampleRate = st.sample_rate, 
    StatsLastUpdate = st.last_updated,
    Rows = st.rows
from (
    select s.object_id, s.name, sp.modification_counter, sp.unfiltered_rows, s.stats_id, sp.last_updated, sample_rate = (100.*sp.rows_sampled)/sp.unfiltered_rows , sp.rows
    from sys.stats s 
    cross apply sys.dm_db_stats_properties(s.object_id,s.stats_id) sp 
) st
left join (
        select ips.object_id, i.name, ips.partition_number, ips.avg_fragmentation_in_percent, i.index_id
        from sys.dm_db_index_physical_stats(db_id(),null, null, null, default) ips
        join sys.indexes i 
            on ips.index_id = i.index_id
            and ips.object_id = i.object_id
        ) idx
on st.object_id = idx.object_id
    and st.stats_id = idx.index_id
join sys.tables t 
    on st.object_id = t.object_id
join sys.schemas s
    on t.schema_id = s.schema_id
where s.name = 'Sales' and t.name = 'SalesOrderDetail'
go
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

好的,我们的碎片数量稍微好一些,但并不重要,在您看来,可能不值得重建而不是重组的开销。

不过,还有一些其他值得注意的事情。

  1. 重建索引也会通过全扫描更新您的统计信息。需要注意的是,它只更新支持索引的统计数据——没有更新其他统计数据(正如 的悲伤状态所证明的那样_WA_Sys_00000006_44CA3770)。
  2. 重新组织您的索引不会以任何容量更新您的统计信息

结论

除了您是否获得良好的碎片整理率之外,还有更多需要评估的内容。虽然对 IO 肯定很重要,但我也发现探索我的系统并看看我是否可以确定如何最好地维护它,而不是盲目地更新、重建和重组,这非常有帮助。