尽可能少地回收分区中的空闲 space

Reclaim free space in partition with as little fragmentation as possible

问题: 在 Sql server 2012 中,回收尽可能多的保留 space 并尽可能减少碎片的最佳方法是什么?

背景:

我们的 SQL 服务器 运行 磁盘空间不足 space 并且作为硬件+软件升级的一部分,我们将把数据文件移动到不同的服务器 - 出于这个原因我们想减少数据文件的大小(以防止 'reserved space' 的不必要移动。我们正在谈论 tearbytes)。我还想逐个分区执行此分区,以便能够 运行 过夜并限制对生产的影响。

我尝试过的一种方法(使用单个索引的重度消费者 table 上的每个分区):

ALTER TABLE <Tbl>
REBUILD PARTITION = <PartitionId> WITH (DATA_COMPRESSION =  PAGE) 
GO

--I know this is bad practice - but I need to reclaim space to speed up moving
DBCC SHRINKFILE(<PartitionName>, target_size = 10 )
GO

-- This is to mitigate the impact of shrinkfile
ALTER TABLE <Tbl>
REBUILD PARTITION = <PartitionId>
GO


--Run this in the end (and I run also between the individual tasks) to see impact on index fragmentation
SELECT * FROM sys.dm_db_index_physical_stats  
    (DB_ID(<DbName>), OBJECT_ID(<TblName>), <IndexId>, <PartitionId>, 'SAMPLED');  
GO

在测试环境中,这对某些分区产生了很好的结果(0% 的碎片化和接近 0% 'wasted' space 在保留 space 上。考虑到下一阶段正在移动数据而浪费了通过电线),但我有一个分区的情况, SHRINKFILE 显着减小了大小,但导致 99.99% 的碎片; REBUILD 解决了碎片问题,但文件组大小加倍(一半被保留 space)——这可能是预期的,因为重建从头开始创建索引。如果我之后收缩我可以回收 space,但再次得到大碎片。这可以绕圈子

我现在正尝试运行重组压缩文件组:

ALTER INDEX <IdxName> on <Tbl> REORGANIZE PARTITION = <PartitionId> 

因为这应该有望在不增加数据文件的情况下修复索引碎片。 然而:

我正在考虑的另一个选择是将分区重建为全新的文件组,但这需要处理分区架构 - 我希望该过程尽可能简单。

如何使用压缩备份数据库并将其恢复到新服务器。备份不包括未使用的 space.

这不是最佳答案,但这是我所做的,因为它最能解决我的具体情况 - 特别是 缺少任何额外的免费 space used - 所以我什至不能使用备份恢复方法; 能够以较小的(隔夜)批量执行操作

我想post它以防有人觉得它有用。

但是 - 确保您始终拥有至少与您的数据库当前占用的空间一样多的space绝对更好,然后您可以使用更合适的解决方案,例如我标记为答案的压缩备份建议。

--This is just so that anything doesn't interract with table during the entire process.
-- reorganize is being done online; but I want the process to finish as fast as possible and
--  app logic is resilient to not seeing the table for while
exec sp_rename  <tbl_orig>, <tbl>
GO

print 'starting compressing: ' + CAST(GETDATE() AS NVARCHAR)
GO

-- this is to enable compression on the partition
ALTER TABLE <tbl> 
REBUILD PARTITION = <PartitionId> WITH (DATA_COMPRESSION =  PAGE) 
GO

print 'Compressing done: ' + CAST(GETDATE() AS NVARCHAR)
GO

-- recaliaming all free space; potentially very bad fragmentation is possible
DBCC SHRINKFILE(<DataFile>, target_size = 10 )
GO

print 'shrinking done: ' + CAST(GETDATE() AS NVARCHAR)
GO

-- solve the fragmentation without giving up on any reclaimed space. Rebuild would use some additional space.
-- This assumes that my partitions are stored in dedicated filegroups (which is always a good idea)
ALTER INDEX <IdxName> on <tbl> REORGANIZE PARTITION = <PartitionId>
GO

print 'index reorganizing done: ' + CAST(GETDATE() AS NVARCHAR)
GO

-- see the stats
SELECT * FROM sys.dm_db_index_physical_stats  
    (DB_ID(<DBName>), OBJECT_ID(<Tbl>), 1, <PartitionId> , 'SAMPLED');  
GO

print 'DONE: ' + CAST(GETDATE() AS NVARCHAR)
GO

-- show the table back to app logic
exec sp_rename  <tbl>, <tbl_orig>
GO

您可以将分区重建到新的文件组。这将产生完美连续的页面和完美填充的文件。这通常是一种非常好的碎片整理方法。您可以将其自动化。

如您所见,通过就地重建进行碎片整理存在一些问题。您需要大量临时 space 并且您新分配的 b-tree 将被 SQL 服务器分配算法压缩成大量空闲 space 空洞。分配算法不智能。它不会试图找到大洞。如果有小洞,它很高兴将新树铺在小洞上。这就是你可能在重建后直接碎片化的原因。 (有趣的是,NTFS 也有同样的问题。如果你只是顺序写入一个 100GB 的文件,它最终可能会非常碎片化。)

我相信这个问题在 SQL 服务器社区中没有得到广泛理解。