我需要缩小数据库-我刚刚释放了很多空间


35

这里以各种形式提出了这个问题,但问题归结为:

我知道缩小数据库是有风险的。在这种情况下,我已经删除了太多数据,并且永远不会再使用它。

  • 如何缩小数据库?我可以缩小哪些文件?
  • 这样做时我应该考虑什么?
  • 之后我应该做什么?
  • 如果是大型数据库怎么办?我可以缩小一点吗?

2
一段时间前,我为此感到挣扎:dba.stackexchange.com/questions/47310/…我试图总结自己的经验,我的回答是
Csaba Toth

Answers:


30

一些最初的警告:

  1. 它通常被称为一个最坏的实践不断缩小生产数据库或数据文件(日志文件是另一个问题,因为这个问题挂在嘴边)。我建议人们不要在这样的博客文章中缩减数据库,在这些文章中我谈到“调整大小”和良好的计划。我并不孤单(Paul RandalBrent Ozar,只是为了提供更多链接)。收缩数据文件或数据库碎片索引,是缓慢而费力的在你的资源,可以为您的系统上的排水,只是一个坏的事情,一般
  2. 在这种情况下,我们都知道存在风险,已经准备好应对它,但是我们释放了很多空间,我们知道我们再也不需要了。因此,在这种特定类型的情况下-缩小是我们的选择之一,在很大程度上是有意义的。

如果你读过有关的问题和风险,你仍然需要,因为你释放一个做到这一点的收缩显著的空间大小,这个答案的希望其余部分将帮助你。但是要考虑一下风险。

这里有两种主要方法,其中两种是考虑的:

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永远不要告诉任何人我缩小了数据库;-)


1
我要添加一种特殊情况,如果您从堆中删除了很多数据(尤其是从堆中间),您将无法获得该空间,除非您向其添加了聚集索引(希望永远如此),然后删除聚簇索引(将其返回到堆之后)。当然,如果定期将堆截断,就不用担心了。但仍然值得一提。
乔纳森·费特

有人可以解释NOTRUNCATE和TRUNCATEONLY的含义,显然后者不会重新排列页面,因此不会导致索引碎片?
大卫·加西亚

4
  1. 如何缩小数据库?我可以缩小哪些文件?:您可以使用DBCC SHRINKFILE您提到的命令分别收缩文件。这取决于您的服务器数据库包含多少个文件。一个简单的数据库具有一个数据库文件和一个事务日志文件。
  2. 这样做时我应该考虑什么?:缩小会影响您的索引碎片,请参阅第3点。还要注意,您不希望将数据库文件缩小到尽可能小的大小,因为在实际环境中,它无论如何都会增长。因此,我将调整大小(在您的示例中为7 MB),您将在数据库文件中保留10%-20%的可用空间,因为无论如何它都会在生产环境中填充,并且您可以这样可以节省一些自动增长周期。因此,实际数字需要仔细计算。还要注意,您执行的“大空间释放”将使事务日志文件膨胀得比在DB文件中获得的空间更多。此外,您可能实际体验到的空间增益将小于您的数学期望!假设您在数学上释放了12个演出,
  3. 之后我应该做什么?:如前所述,您想重新索引那些由于SHRINK的更改而导致碎片变形的索引。如果您需要对查询统计信息做一些特别的事情,我还没有做足够的试验。
  4. 如果是大型数据库怎么办?我可以缩小一点吗?SHRINK操作可以随时中断,您可以稍后继续。如果可能,我建议在离线数据库上执行它。通过打断和控制,虽然可以缩小尺寸。从理论上讲,您可以通过指定较小的目标大小(而不是7兆字节)来以较小的增量进行缩小,但是我想说的是,如果您在生产中执行它,则只给它一次。如您所见,索引碎片和可能的事务日志增长存在问题。所以我只经历了一次。

我们都知道,不建议定期进行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。*模式中的表感兴趣。


2
对于索引重建,请注意在SQL 2005中不推荐使用DBREINDEX。您可以使用:EXEC sp_MSForeachtable @ Command1 =“ ALTER INDEX ALL ON?REBUILD”,而不是带有游标的庞大脚本。希望对您有所帮助。
KISS

2

您已经听到了有关收缩数据库的所有警告,它们都是对的。它会碎片化您的索引,并且通常会破坏数据库,因此不应在生产系统上完成。

但是,由于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

1

下面的引号直接来自Microsoft(适用于2008-2016版),并提供有关是否/何时以及如何使用该DBCC SHRINKFILE命令的指南。

https://msdn.microsoft.com/zh-CN/library/ms189493.aspx

最佳实践

计划收缩文件时,请考虑以下信息:

  • 在创建大量未使用空间的操作(例如截断表或删除表操作)之后,收缩操作最有效。
  • 大多数数据库都需要一些可用空间才能用于常规的日常操作。如果您反复收缩数据库,并发现数据库大小再次增加,则表明常规操作需要收缩的空间。在这些情况下,重复收缩数据库是浪费的操作。
  • 收缩操作不会保留数据库中索引的碎片状态,通常会将碎片增加到一定程度。这是不重复收缩数据库的另一个原因。
  • 顺序而不是同时收缩同一数据库中的多个文件。系统表上的争用可能由于阻塞而导致延迟。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.