DBCC SHRINKFILE (Transact-SQL)

适用于: SQL Server Azure SQL 数据库 Azure SQL 托管实例

收缩当前数据库的指定数据或日志文件大小。 可以使用它将一个文件中的数据移到同一文件组中的其他文件,这会清空文件,从而允许删除数据库。 可以将文件收缩到小于创建大小,同时将最小文件大小重置为新值。 仅在必要时使用 DBCC SHRINKFILE。

注意

收缩操作不应被视为常规维护操作。 由于常规定期业务操作而增长的数据和日志文件不需要收缩操作。

Transact-SQL 语法约定

语法

DBCC SHRINKFILE   
(  
    { file_name | file_id }   
    { [ , EMPTYFILE ]   
    | [ [ , target_size ] [ , { NOTRUNCATE | TRUNCATEONLY } ] ]  
    }  
)  
[ WITH 
  {     
      [ WAIT_AT_LOW_PRIORITY 
        [ ( 
            <wait_at_low_priority_option_list>
        )] 
      ] 
      [ , NO_INFOMSGS]
  }
]
       
< wait_at_low_priority_option_list > ::=  
    <wait_at_low_priority_option>
    | <wait_at_low_priority_option_list> , <wait_at_low_priority_option>
 
< wait_at_low_priority_option > ::=
    ABORT_AFTER_WAIT = { SELF | BLOCKERS }

参数

file_name

要收缩的文件的逻辑名称。

file_id

要收缩的文件的标识 (ID) 号。 若要获取文件 ID,请使用 FILE_IDEX 系统函数,或查询当前数据库中的 sys.database_files 目录视图。

target_size

整数,表示文件的新大小(以 MB 为单位)。 如果未指定或为 0,DBCC SHRINKFILE 缩小到文件创建大小。

可以使用 DBCC SHRINKFILE <target_size> 缩小空文件的默认大小。 例如,如果创建一个 5 MB 的文件,然后在文件仍然为空的时候将文件收缩为 3 MB,默认文件大小将设置为 3 MB。 这只适用于永远不会包含数据的空文件。

FILESTREAM 文件组容器不支持此选项。

如果 target_size 已指定,DBCC SHRINKFILE 会尝试将文件收缩到目标大小。 要释放的文件区域中的已用页移到文件保留区域中的可用空间。 例如,对于 10 MB 数据文件,target_size 为 8 的 DBCC SHRINKFILE 操作会将文件最后 2 MB 中的所有已用页移到文件前 8 MB 中的任何未分配页中。 DBCC SHRINKFILE 不会收缩已超过所需存储数据大小的文件。 例如,如果使用 10 MB 数据文件中的 7 MB,则带有 target_size 为 6 的 DBCC SHRINKFILE 语句只能将该文件收缩到 7 MB,而不能收缩到 6 MB。

EMPTYFILE

将指定文件中的所有数据迁移到同一文件组中的其他文件。 也就是说,EMPTYFILE 将指定文件中的数据迁移到同一文件组中的其他文件。 EMPTYFILE 确保不会将任何新数据添加到文件中(尽管此文件不是只读文件)。 可以使用 ALTER DATABASE 语句删除文件。 如果你使用 ALTER DATABASE 语句更改文件大小,只读标志会重置,并能添加数据。

对于 FILESTREAM 文件组容器,无法使用 ALTER DATABASE 删除文件,除非 FILESTREAM 垃圾回收器已运行,并删除了 EMPTYFILE 已复制到另一个容器的所有不必要文件组容器文件。 有关详细信息,请参阅 sp_filestream_force_garbage_collection。 有关删除 FILESTREAM 容器的信息,请参阅 ALTER DATABASE 文件和文件组选项 (Transact-SQL) 中的相应章节

Azure SQL 数据库或Azure SQL 数据库“超大规模”不支持 EMPTYFILE。

NOTRUNCATE

无论是否指定 target_percent,将数据文件末尾中的已分配页移到文件开头的未分配页区域中。 操作系统不会回收文件末尾的可用空间,文件的物理大小也不会改变。 因此,如果指定 NOTRUNCATE,文件看起来就像没有收缩一样。

NOTRUNCATE 只适用于数据文件。 日志文件不受影响。

FILESTREAM 文件组容器不支持此选项。

TRUNCATEONLY

将文件末尾的所有可用空间释放给操作系统,但不在文件内部移动任何页。 数据文件只收缩到最后分配的区。

如果使用 TRUNCATEONLY 指定,则会忽略 target_size。

TRUNCATEONLY 选项不会移动日志中的信息,但会删除日志文件末尾的失效 VLF。 FILESTREAM 文件组容器不支持此选项。

WITH NO_INFOMSGS

取消显示所有信息性消息。

收缩操作的 WAIT_AT_LOW_PRIORITY

适用于:SQL Server 2022 (16.x) 及更高版本、Azure SQL 数据库和 Azure SQL 托管实例

低优先级等待功能减少了锁争用。 有关详细信息,请参阅了解 DBCC SHRINKDATABASE 的并发问题

此功能与联机索引操作的 WAIT_AT_LOW_PRIORITY 类似,但有一些差异。

  • 无法指定 ABORT_AFTER_WAIT 选项“无”。

WAIT_AT_LOW_PRIORITY

适用于:SQL Server(SQL Server 2022 (16.x) 及更高版本)和 Azure SQL 数据库。

在 WAIT_AT_LOW_PRIORITY 模式下执行收缩命令时,在收缩操作停止等待并开始执行之前,等待收缩操作不会阻止需要架构稳定性 (Sch-S) 锁的新查询。 收缩操作将在其能够获取架构修改 (Sch-M) 锁时执行。 在 WAIT_AT_LOW_PRIORITY 模式下,如果新的收缩操作由于长时间运行的查询而无法获取锁,则收缩操作最终会在 1 分钟(默认)后超时,并且会安静地退出。

在 WAIT_AT_LOW_PRIORITY 模式下,如果新的收缩操作由于长时间运行的查询而无法获取锁,则收缩操作最终会在 1 分钟(默认)后超时,并且会安静地退出。 如果由于并发查询或持有 Sch-S 锁的查询导致收缩操作而无法获取 Sch-M 锁,就会出现这种情况。 如果发生超时,系统将向 SQL Server 错误日志发送错误 49516 消息,例如:Msg 49516, Level 16, State 1, Line 134 Shrink timeout waiting to acquire schema modify lock in WLP mode to process IAM pageID 1:2865 on database ID 5。 此时,只需在 WAIT_AT_LOW_PRIORITY 模式下重试收缩操作,因为已知应用程序不会受到影响。

ABORT_AFTER_WAIT = [ SELF | BLOCKERS ]

适用于:SQL Server(SQL Server 2022 (16.x) 及更高版本)和 Azure SQL 数据库。

  • SELF

    不采取任何操作,直接退出当前执行的收缩文件操作。

  • BLOCKERS

    终止阻塞收缩文件操作的所有用户事务,使操作可继续进行。 BLOCKERS 选项要求登录名拥有 ALTER ANY CONNECTION 权限。

结果集

下表描述了结果集列。

列名称 说明
DbId 数据库引擎试图收缩的文件的数据库标识号。
FileId 数据库引擎试图收缩的文件的文件标识号。
CurrentSize 文件当前占用的 8 KB 页数。
MinimumSize 文件最低可以占用的 8 KB 页数。 此数字对应于文件的大小下限或最初创建大小。
UsedPages 文件当前使用的 8 KB 页数。
EstimatedPages 数据库引擎估计文件能够收缩到的 8 KB 页数。

备注

DBCC SHRINKFILE 适用于当前数据库的文件。 有关如何更改当前数据库的详细信息,请参阅 USE (Transact-SQL)

可以随时停止执行 DBCC SHRINKFILE 操作,并保留任何已完成的工作。 如果你使用 EMPTYFILE 参数并取消操作,文件不会被标记,以防添加其他数据。

DBCC SHRINKFILE 操作失败时,则会引发错误。

其他用户可以在文件收缩期间使用数据库,数据库不必处于单用户模式。 无需在单用户模式下运行 SQL Server 实例,即可收缩系统数据库。

使用 WAIT_AT_LOW_PRIORITY 指定时,收缩操作的 Sch-M 锁定请求将在执行命令 1 分钟时按低优先级等待。 如果在此期间,操作被阻止,将执行指定的 ABORT_AFTER_WAIT 操作。

超大规模 Azure SQL 数据库的数据库和文件收缩操作目前以预览版提供。 有关预览版的详细信息,请参阅超大规模 Azure SQL 数据库的收缩

已知问题

适用范围:SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Azure Synapse Analytics 专用 SQL 池

  • 目前,压缩列存储段中的 LOB 列类型(varbinary(max)varchar(max)nvarchar(max)不受和DBCC SHRINKFILE影响DBCC SHRINKDATABASE

了解 DBCC SHRINKFILE 的并发问题

收缩数据库和收缩文件命令可能会导致并发问题,尤其是在主动维护(例如重新生成索引)时或繁忙的 OLTP 环境中。 当应用程序对数据库表执行查询时,这些查询将获取和维护架构稳定性锁 (Sch-S),直到查询完成其操作。 尝试在常规使用期间回收空间时,收缩数据库和收缩文件操作当前需要在移动或删除索引分配映射 (IAM) 页时使用架构修改锁 (Sch-M),从而阻止用户查询所需的 Sch-S 锁。 因此,长时间运行的查询将阻止收缩操作,直到查询完成。 这意味着任何需要 Sch-S 锁的新查询也都会在等待收缩操作之后排队,并且也会被阻止,这进一步加剧了这种并发问题。 这可能会严重影响应用程序查询性能,也会对完成必要的维护以收缩数据库文件造成困难。 SQL Server 2022 (16.x) 中引入了收缩低优先级等待功能,通过在 WAIT_AT_LOW_PRIORITY 模式下获取架构修改锁解决了这个问题。 有关详细信息,请参阅收缩操作的 WAIT_AT_LOW_PRIORITY

有关 Sch-S 和 Sch-M 锁的详细信息,请参阅事务锁定和行版本控制指南

收缩日志文件

对于日志文件,数据库引擎 使用 target_size 计算整个日志的目标大小。 因此,target_size 是执行收缩操作后的日志可用空间。 随后,整个日志的目标大小转换为每个日志文件的目标大小。 DBCC SHRINKFILE 尝试立即将每个物理日志文件收缩到其目标大小。 但是,如果部分逻辑日志位于超出目标大小的虚拟日志中,则数据库引擎将释放尽可能多的空间,并发出一条信息性消息。 该消息说明需要执行哪些操作来将逻辑日志移出位于文件末尾的虚拟日志。 执行操作后,DBCC SHRINKFILE 可用于释放剩余空间。

因为日志文件只能收缩到虚拟日志文件边界,所以可能无法将日志文件收缩到小于虚拟日志文件(即使没在使用它)。 数据库引擎在日志文件创建或扩展时,动态选择虚拟日志文件大小。

最佳做法

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

  • 在执行会产生大量未用空间的操作(如截断表或删除表操作)后,执行收缩操作最有效。

  • 大多数数据库都需要一些可用空间,以供常规日常操作使用。 如果反复收缩数据库文件并注意到数据库大小再次变大,则表明常规操作需要可用空间。 在这种情况下,反复收缩数据库文件是一种无谓的操作。 增加数据库文件所需的自动增长事件会影响性能。

  • 收缩操作不保留数据库中索引的碎片状态,通常还会在一定程度上增加碎片。 此类碎片是不要反复收缩数据库的另一个原因。

  • 按顺序而非同时缩小同一数据库中的多个文件。 对系统表的争用可能会导致阻塞,进而导致延迟。

疑难解答

本部分介绍如何诊断和更正在运行 DBCC SHRINKFILE 命令时可能发生的问题。

文件不收缩

如果在执行无错误收缩操作后文件大小未改变,请尝试执行以下操作,验证文件是否有足够的可用空间:

  • 运行以下查询。
SELECT name
    , size / 128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT) / 128.0 AS AvailableSpaceInMB
FROM sys.database_files;
  • 运行 DBCC SQLPERF 命令以返回事务日志中使用的空间。

如果可用空间不足,收缩操作无法进一步缩小文件大小。

通常情况下,似乎不收缩的是日志文件。 导致这种不收缩的原因通常是,日志文件尚未截断。 若要截断日志,可以将数据库恢复模式设置为 SIMPLE,或者先备份日志,再重新运行 DBCC SHRINKFILE 操作。

收缩操作受阻

基于行版本控制的隔离级别下运行的事务可能会阻止收缩操作。 例如,如果在执行 DBCC SHRINKDATABASE 操作时,正在基于行版本控制的隔离级别下运行大型删除操作,那么收缩操作会等到删除操作完成,然后才会继续。 发生此阻塞时,DBCC SHRINKFILEDBCC SHRINKDATABASE 操作会将提示消息(5202 表示 SHRINKDATABASE,5203 表示 SHRINKFILE)打印到 SQL Server 错误日志。 在第一个小时内,此消息每五分钟记录一次,之后每一小时记录一次。 例如,如果错误日志包含以下错误消息,则会发生以下错误:

DBCC SHRINKFILE for file ID 1 is waiting for the snapshot
transaction with timestamp 15 and other snapshot transactions linked to
timestamp 15 or with timestamps older than 109 to finish.

此消息指明,时间戳早于 109(收缩操作完成的最后一个事务)的快照事务正在阻止收缩操作。 它还指明,sys.dm_tran_active_snapshot_database_transactions 动态管理视图中的 transaction_sequence_numtransaction_sequence_num 或 first_snapshot_sequence_numfirst_snapshot_sequence_num 列包含值 15。 如果 transaction_sequence_numfirst_snapshot_sequence_num 视图列包含的数字小于收缩操作完成的最后一个事务 (109),则收缩操作会等待这些事务完成。

若要解决此问题,请执行下列任务之一:

  • 终止阻止收缩操作的事务。
  • 终止收缩操作。 如果收缩操作终止,所有已完成的工作都会保留。
  • 不执行任何操作,并允许收缩操作等到阻塞事务完成。

权限

要求具有 sysadmin 固定服务器角色或 db_owner 固定数据库角色的成员身份。

示例

A. 将数据文件收缩到指定的目标大小

以下示例将 UserDB 用户数据库中名为 DataFile1 的数据文件的大小收缩到 7 MB。

USE UserDB;
GO
DBCC SHRINKFILE (DataFile1, 7);
GO

B. 将日志文件收缩到指定的目标大小

以下示例将 AdventureWorks2022 数据库中的日志文件收缩到 1 MB。 若要允许 DBCC SHRINKFILE 命令收缩文件,首先需要通过将数据库恢复模式设置为 SIMPLE 来截断该文件。

USE AdventureWorks2022;
GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE AdventureWorks2022
SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DBCC SHRINKFILE (AdventureWorks2022_Log, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE AdventureWorks2022
SET RECOVERY FULL;
GO

C. 截断数据文件

下面的示例将截断 AdventureWorks2022 数据库中的主数据文件。 需要查询 sys.database_files 目录视图以获得数据文件的 file_id

USE AdventureWorks2022;
GO
SELECT file_id, name
FROM sys.database_files;
GO
DBCC SHRINKFILE (1, TRUNCATEONLY);

D. 清空文件

下面的示例展示了如何清空文件,这样文件就能从数据库中删除。 为了方便此示例进行展示,先创建包含数据的数据文件。

USE AdventureWorks2022;
GO
-- Create a data file and assume it contains data.
ALTER DATABASE AdventureWorks2022
ADD FILE (
    NAME = Test1data,
    FILENAME = 'C:\t1data.ndf',
    SIZE = 5MB
    );
GO
-- Empty the data file.
DBCC SHRINKFILE (Test1data, EMPTYFILE);
GO
-- Remove the data file from the database.
ALTER DATABASE AdventureWorks2022
REMOVE FILE Test1data;
GO

E. 使用 WAIT_AT_LOW_PRIORITY 收缩数据库文件

以下示例尝试将当前用户数据库中的数据文件的大小收缩到 1 MB。 需要查询 sys.database_files 目录视图以获得数据文件的 file_id,在本例中为 file_id 5。 如果无法在一分钟内获取锁,收缩操作将中止。

USE AdventureWorks2022;
GO

SELECT file_id, name
FROM sys.database_files;
GO

DBCC SHRINKFILE (5, 1) WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = SELF);