截断了 200GB 的表,但未释放磁盘空间

Luc*_*ena 25 sql-server truncate sql-server-2012

我只剩下 2GB 了,所以我需要删除这个历史表。该表现在为空,但数据库磁盘空间未释放。并且数据库文件是320GB。

Tho*_*ger 26

如果您要引用卷上的实际数据库文件消耗,则SQL Server 不会自动处理。仅仅因为您从数据库中删除了数据并不意味着数据库文件将缩小以仅适合现有数据。

您要寻找的是,如果您必须回收卷上的空间,将使用DBCC SHRINKFILE. 根据该文档,值得注意的是一些最佳实践:

最佳实践

当您计划缩小文件时,请考虑以下信息:

  • 在创建大量未使用空间的操作(例如截断表或删除表操作)之后,收缩操作最有效。

  • 大多数数据库都需要一些可用空间来用于常规的日常操作。如果重复收缩数据库,发现数据库大小再次增长,则表明收缩的空间是常规操作所必需的。在这些情况下,反复收缩数据库是一种浪费的操作。

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

  • 顺序收缩同一数据库中的多个文件,而不是同时收缩。系统表上的争用可能会因阻塞而导致延迟。

另外值得注意的是:

DBCC SHRINKFILE 操作可以在过程中的任何一点停止,并保留任何已完成的工作。

执行此操作时肯定需要考虑一些事项,我建议您查看Paul Randal 的博客文章,了解执行此操作时会发生什么。

第一步肯定是验证您实际上可以替换多少空间和可用空间,以及文件上的已用空间:

use AdventureWorks2012;
go

;with db_file_cte as
(
    select
        name,
        type_desc,
        physical_name,
        size_mb = 
            convert(decimal(11, 2), size * 8.0 / 1024),
        space_used_mb = 
            convert(decimal(11, 2), fileproperty(name, 'spaceused') * 8.0 / 1024)
    from sys.database_files
)
select
    name,
    type_desc,
    physical_name,
    size_mb,
    space_used_mb,
    space_used_percent = 
        case size_mb
            when 0 then 0
            else convert(decimal(5, 2), space_used_mb / size_mb * 100)
        end
from db_file_cte;
Run Code Online (Sandbox Code Playgroud)


Sha*_*nky 6

这是截断表时的正常行为,并且涉及删除超过 128 个范围作为Per Books Online

当您删除或重建大型索引,或者删除或截断大型表时,数据库引擎会推迟实际的页面释放及其关联的锁,直到事务提交之后。此实现支持多用户环境中的自动提交和显式事务,并适用于使用超过 128 个区的大型表和索引。

数据库引擎通过将进程拆分为两个单独的阶段(逻辑和物理)来避免删除大对象所需的分配锁。

在逻辑阶段,表或索引使用的现有分配单元被标记为释放并锁定,直到事务提交。删除聚集索引后,数据行被复制,然后移动到创建的新分配单元,以重新构建的聚集索引或堆存储。(在索引重建的情况下,数据行也被排序。)当有回滚时,只需要回滚这个逻辑阶段。

物理阶段发生在事务提交之后。标记为取消分配的分配单元被分批物理丢弃。这些丢弃在后台发生的短事务中处理,不需要大量锁。

由于物理阶段发生在事务提交之后,因此表或索引的存储空间可能仍会显示为不可用。如果在物理阶段完成之前数据库增长需要此空间,则数据库引擎会尝试从标记为取消分配的分配单元中恢复空间。要查找这些分配单元当前使用的空间,请使用 sys.allocation_units 目录视图。

延迟删除操作不会立即释放分配的空间,并且会在数据库引擎中引入额外的开销成本。因此,使用 128 个或更少范围的表和索引将被删除、截断和重建,就像在 SQL Server 2000 中一样。这意味着逻辑和物理阶段都发生在事务提交之前。

您将不得不等待,当然,您必须手动缩小文件以回收空间,另外值得一提的是,缩小会导致逻辑碎片,除非您的需求很严重,否则应该避免。您必须在缩小和碎片化之间取得某种平衡。考虑到数据无论如何都会再次增长的情况,最好问问自己收缩是否真的能解决问题。

使用下面的查询来检查数据库中有多少可用空间

SELECT name ,size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS AvailableSpaceInMB
FROM sys.database_files;
Run Code Online (Sandbox Code Playgroud)


sta*_*ray 6

除了 Tom 和 Shanky 的回答之外,如果您的数据库包含 LOB/BLOB 数据,则 DBCC SHRINKFILE 可能不起作用。如果是这种情况,那么您有两个选择,具体取决于您是否可以使数据库脱机。如果您可以使数据库脱机,则您需要将数据复制出来,然后再复制回来以删除空白空间。您可以通过以下方法之一完成此操作:

  1. 使用SELECT INTO语句将整个表转移到新表。删除原始表,运行DBCC SHRINKFILE。将新表重命名为原始表名。
  2. 使用 bcp 在本机模式下复制表,删除表,运行DBCC SHRINKFILE,创建表,然后将数据 bcp 到表中。
  3. 使用导出/导入将所有数据移动到新数据库,删除现有数据库,将新数据库重命名为原始数据库名称。

如果您无法使数据库脱机,则可以使用带有EMPTYFILE选项的DBCC SHRINKFILE命令。

离线复制的详细信息:http : //support.microsoft.com/kb/324432/en-us

EMPTYFILE 选项的当前信息 http://msdn.microsoft.com/en-us/library/ms189493(v=sql.105).aspx