Answers:
一些最初的警告:
如果你读过有关的问题和风险,你仍然需要,因为你释放一个做到这一点的收缩显著的空间大小,这个答案的希望其余部分将帮助你。但是要考虑一下风险。
这里有两种主要方法,其中两种是考虑的:
1.)收缩 是,进行实际的收缩 -考虑使用DBCC SHRINKFILE
代替DBCC SHRINKDATABASE
,您可以更好地控制收缩的内容和方式。这肯定会导致性能下降-这是一个很大的操作,需要大量的IO。你可以潜在地逃脱反复收缩到渐进地小的目标大小。
这是上面DBCC SHRINKFILE
链接中的“ A.)”示例。在此示例中,数据文件缩小为7MB目标大小。这种格式是在停机时间窗口允许的情况下反复缩小的一种好方法。我将在开发测试中进行此操作,以查看性能如何以及可以增加/降低的幅度,并确定预期的生产时间。这是一项联机操作-您可以与正在访问正在收缩的数据库的系统中的用户一起运行它,但是性能会降低,几乎可以保证。因此,理想情况下,监视并观察并查看对服务器的操作,选择停机时间窗口或活动较少的时间段。
USE YourDatabase;
GO
DBCC SHRINKFILE (DataFile1, 7);
GO
永远记住: -每次收缩时,都会使索引碎片化,如果要在较长时间内收缩成块,则应该进行索引重建。如果您无法在一个窗口中全部完成,则每次都将产生该费用。
2.)新数据库 -您可以创建一个新数据库并将数据迁移到该数据库。您必须将空数据库及其所有键,索引,对象,proc,函数等编写成脚本,然后将数据迁移到该数据库。您可以为此编写脚本,也可以使用Red Gate或其他供应商提供的类似SQL Data Compare之类的工具。这是更多的设置工作,需要您进行更多的开发和测试,并且取决于您的环境,这也可能会使您的停机时间窗口变大,但您可以考虑使用。
当我被迫收缩数据库时 如果我的环境是这样,我希望在数据文件中保留相当大的空白空间,因为我喜欢做磁盘生猪,并且愿意为将来的/意外的增长做准备。所以,我会好起来的捐赠领域回来,如果我们只是删除了大部分的空间,但我从来没有相信那些说“但它绝不会再次增长”,仍然留下一些空白。我可能会选择的路线(叹息)是收缩方法,如果我的停机时间窗口较小,并且不想增加创建空数据库并将数据迁移到数据库的复杂性。因此,我会逐步缩小它一堆(基于我认为需要多少次基于开发环境中的测试和所需的大小。逐步选择较小的文件大小),然后重新构建索引。然后我d永远不要告诉任何人我缩小了数据库;-)
DBCC SHRINKFILE
您提到的命令分别收缩文件。这取决于您的服务器数据库包含多少个文件。一个简单的数据库具有一个数据库文件和一个事务日志文件。我们都知道,不建议定期进行SHRINK。我会尽量省略您可能仍然知道的所有警告和免责声明。备份,如果可能的话,不要在家中进行此操作:)
奖励:在复制环境中,如果您在发布者数据库上执行此操作,则不会导致订阅者数据库收缩(由于它们是Express版本,因此可能会出现大小问题)。
最后,我的重新编制脚本:
USE YourDBName
DECLARE @TbName VARCHAR(255)
DECLARE @FullTbName VARCHAR(255)
DECLARE @IxName VARCHAR(255)
DECLARE myCursor CURSOR FOR
SELECT OBJECT_NAME(dmi.object_id) AS TableName,i.name AS IndexName
FROM sys.dm_db_index_physical_stats(14, NULL, NULL, NULL , 'LIMITED') dmi
JOIN sys.indexes i on dmi.object_id = i.object_id and dmi.index_id = i.index_id
WHERE avg_fragmentation_in_percent > 30
ORDER BY avg_fragmentation_in_percent
OPEN myCursor
FETCH NEXT FROM myCursor INTO @TbName, @ixName
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dba' AND TABLE_NAME = @TbName)
BEGIN
SET @FullTbName = 'dba.' + @TbName
IF (@ixName IS NULL)
BEGIN
PRINT 'Reindexing Table ' + @FullTbName
DBCC DBREINDEX(@FullTbName, '', 0)
END
ELSE
BEGIN
PRINT 'Reindexing Table ' + @FullTbName + ', Index ' + @IxName
DBCC DBREINDEX(@FullTbName, @IxName, 0)
END
END
FETCH NEXT FROM myCursor INTO @TbName, @ixName
END
CLOSE myCursor
DEALLOCATE myCursor
此变量中唯一的变量是14,可以通过发出select来获得DB_ID('YourDBName')
,脚本假定您仅对dba。*模式中的表感兴趣。
您已经听到了有关收缩数据库的所有警告,它们都是对的。它会碎片化您的索引,并且通常会破坏数据库,因此不应在生产系统上完成。
但是,由于SSD驱动器上的空间不足,通常我每周都会在工作站上恢复备份时这样做。提醒您,我不是写此脚本,而是几年前找到的。在其他数据库[250 GB]上,我创建了一个SSIS程序包,该程序包将传输所需的表,然后为该索引重新创建索引,以便让索引具有全新的感觉。
DECLARE @DBFileName SYSNAME
DECLARE @TargetFreeMB INT
DECLARE @ShrinkIncrementMB INT
SET @DBFileName = 'Set Name of Database file to shrink'
-- Set Desired file free space in MB after shrink
SET @TargetFreeMB = 500
-- Set Increment to shrink file by in MB
SET @ShrinkIncrementMB = 100
SELECT [FileSizeMB] = convert(NUMERIC(10, 2),
round(a.size / 128., 2)),
[UsedSpaceMB] = convert(NUMERIC(10, 2),
round(fileproperty(a.NAME, 'SpaceUsed') / 128., 2)),
[UnusedSpaceMB] = convert(NUMERIC(10, 2),
round((a.size - fileproperty(a.NAME, 'SpaceUsed')) / 128., 2)),
[DBFileName] = a.NAME
FROM sysfiles a
DECLARE @sql VARCHAR(8000)
DECLARE @SizeMB INT
DECLARE @UsedMB INT
SELECT @SizeMB = size / 128.
FROM sysfiles
WHERE NAME = @DBFileName
SELECT @UsedMB = fileproperty(@DBFileName, 'SpaceUsed') / 128.
SELECT [StartFileSize] = @SizeMB
,[StartUsedSpace] = @UsedMB
,[DBFileName] = @DBFileName
WHILE @SizeMB > @UsedMB + @TargetFreeMB + @ShrinkIncrementMB
BEGIN
SET @sql = 'dbcc shrinkfile ( ' + @DBFileName + ', ' + convert(VARCHAR(20), @SizeMB - @ShrinkIncrementMB) + ' ) '
PRINT 'Start ' + @sql
PRINT 'at ' + convert(VARCHAR(30), getdate(), 121)
EXEC (@sql)
PRINT 'Done ' + @sql
PRINT 'at ' + convert(VARCHAR(30), getdate(), 121)
SELECT @SizeMB = size / 128.
FROM sysfiles
WHERE NAME = @DBFileName
SELECT @UsedMB = fileproperty(@DBFileName, 'SpaceUsed') / 128.
SELECT [FileSize] = @SizeMB
,[UsedSpace] = @UsedMB
,[DBFileName] = @DBFileName
END
SELECT [EndFileSize] = @SizeMB
,[EndUsedSpace] = @UsedMB
,[DBFileName] = @DBFileName
SELECT [FileSizeMB] = convert(NUMERIC(10, 2), round(a.size / 128., 2))
,[UsedSpaceMB] = convert(NUMERIC(10, 2), round(fileproperty a.NAME, 'SpaceUsed') / 128., 2))
,[UnusedSpaceMB] = convert(NUMERIC(10, 2), round((a.size - fileproperty(a.NAME, 'SpaceUsed')) / 128., 2))
,[DBFileName] = a.NAME
FROM sysfiles a
下面的引号直接来自Microsoft(适用于2008-2016版),并提供有关是否/何时以及如何使用该DBCC SHRINKFILE
命令的指南。
https://msdn.microsoft.com/zh-CN/library/ms189493.aspx
最佳实践
计划收缩文件时,请考虑以下信息:
- 在创建大量未使用空间的操作(例如截断表或删除表操作)之后,收缩操作最有效。
- 大多数数据库都需要一些可用空间才能用于常规的日常操作。如果您反复收缩数据库,并发现数据库大小再次增加,则表明常规操作需要收缩的空间。在这些情况下,重复收缩数据库是浪费的操作。
- 收缩操作不会保留数据库中索引的碎片状态,通常会将碎片增加到一定程度。这是不重复收缩数据库的另一个原因。
- 顺序而不是同时收缩同一数据库中的多个文件。系统表上的争用可能由于阻塞而导致延迟。