问题: 在Sql server 2012中,尽可能少的碎片回收尽可能多的保留空间的最佳方法是什么?
背景
我们的SQL服务器磁盘空间不足,作为HW + SW升级的一部分,我们要将数据文件移动到不同的服务器 - 因此我们希望减少数据文件的大小(以防止不必要的移动'保留'空间'。我们谈论的是泪流氓)。我还希望通过分区执行此分区,以便能够运行过夜并限制生产影响。
我尝试过的一种方法(使用单个索引的重度消费者表上的每个分区):
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%的'浪费'空间。考虑到下一阶段是通过线路移动数据而浪费了),但我有一个分区的情况SHRINKFILE显着减小了大小,但导致99.99%的碎片; REBUILD解决了碎片问题,但是文件组大小加倍(一半是保留空间) - 这可能是因为rebuild从头开始创建索引。如果我之后收缩,我可以收回空间,但再次得到大的碎片。这可以进入圈子
我现在正试图在缩小的文件组上运行重组:
ALTER INDEX <IdxName> on <Tbl> REORGANIZE PARTITION = <PartitionId>
因为这应该有希望修复索引碎片而不增加数据文件。 但是:
我正在考虑的另一个选择是将分区重建为全新的文件组,但这需要操作分区模式 - 我希望尽可能简化流程。
答案 0 :(得分:1)
如何使用压缩备份数据库并将其还原到新服务器。备份不包括未使用的空间。
答案 1 :(得分:0)
这不是最好的答案,但这是我所做的,因为它最好地解决了我的具体情况 - 尤其是没有任何额外的可用空间 - 所以我无法做到甚至使用备份恢复方法;和能够在较小(隔夜)批次中执行操作。
我想张贴以防有人发现它有用。
然而 - 确保您始终拥有至少与您的数据库当前占用的空间一样多的空间,然后您可以使用更合适的解决方案,例如,我标记为答案的压缩备份建议。
--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
答案 2 :(得分:0)
您可以将分区重建为新文件组。这将产生完美连续的页面和完美填充的文件。这通常是一种非常好的碎片整理方式。你可以自动化。
通过重建进行碎片整理有一些问题,如您所知。您需要大量的临时空间,并且您新分配的b树将被SQL Server分配算法压缩成大量可用空间漏洞。分配算法不聪明。它不会试图找到大洞。如果它们存在,它很乐意将新树蔓延到小洞上。这就是你在重建后最终可能被直接分割的原因。 (有趣的是,NTFS也存在同样的问题。如果你只是顺序写一个100GB的文件,它可能最终会非常分散。)
我认为这个问题在SQL Server社区中并没有被广泛理解。