DBCC SHRINKFILE的进展

时间:2013-11-26 16:25:57

标签: c# sql-server sql-server-2012

我有一个数据库21 Gb;其中20 Gb是文件(FileStream),我删除了表中的所有文件,但当我进行备份时,备份文件仍为21 GB

为了解决这个问题,我成了“释放未使用空间”的想法。

所以我正在尝试缩小我的数据库,如下所示:

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

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

如果我在XX分钟后对数据库进行备份,那么备份文件大小为1 Gb,这样我可以看到已经成功清理了未使用的空间。换句话说,上面的Sql代码正常工作( XX分钟后的数据库是)。


问题我需要等到这个查询(缩小操作)完成,所以我尝试执行以下操作:

SELECT percent_complete, start_time, status, command, estimated_completion_time, cpu_time, total_elapsed_time
FROM sys.dm_exec_requests

我在上述查询的结果中找不到有关SHRINKFILE命令的任何信息。

enter image description here


我做错了什么?为什么我看不到DB收缩操作的进度?

我的主要问题是:我怎么能等到SHRINKFILE完成? 例如,我可以从我的C#代码查询发送,在此查询的结果中,我将获得SHRINKFILE操作未完成的信息吗?

2 个答案:

答案 0 :(得分:17)

衡量DBCC SHRINKFILE进度的问题在于,引擎无法确定收缩文件需要完成多少工作。要理解这一点,就要了解DBCC SHRINKFILE的工作原理。基本上,这个过程是:

  • 您声明要将文件缩小到某个点(例如5 GB, 低于10 GB)。
  • 引擎将开始将文件末尾的页面移动到文件开头附近的下一个空白处。

Shrink File Movement

  • 引擎继续运行,直到A)它在您声明的点下移动足够的页面,它可以减小文件大小或B)所有空白区域都在文件的后端。

那为什么这意味着SQL Server不知道需要做多少工作?因为它不知道文件中的空白空间是多么碎片化。如果事情相当紧凑并且靠近文件的前面,收缩文件将很快进行。如果没有,可能需要很长时间。好消息是,一旦页面在文件中移动,它们就会被移动。取消收缩文件不会撤消/回滚此工作,因此如果缩放文件运行一段时间,然后在它完成之前将其终止,则所有页面移动都保持不变。这意味着您可以在几乎您停止的位置重新启动收缩文件(禁止在文件中创建任何新页面)。

答案 1 :(得分:0)

我以不同的方式解决了这个问题,但是这个解决方案不需要任何轮询或等待线程,这是非常实践的。

此方法不会重新组织表索引,只会从硬盘中删除流文件,并将可用空间回收到操作系统。

重要知道以下代码将在同一个线程上工作;因此,这不会影响收缩过程进度,而只是在应用程序线程上运行它。

    var db = EFDbContext;
    try 
    {
        db.ExecuteSqlCommand(@"USE [master]

                                   ALTER DATABASE DatabaseName
                                   SET RECOVERY SIMPLE");

        db.ExecuteSqlCommand(@"USE [master]

                                   EXEC sp_filestream_force_garbage_collection 'DatabaseName'");

        db.ExecuteSqlCommand(@"USE [master]

                                   EXEC sp_filestream_force_garbage_collection 'DatabaseName'");
      }

    }
    catch (Exception e)
    {
      throw new DatabaseException(e.Message, e);
    }
    finally
    {
             db.ExecuteSqlCommand(@"USE [master]
                                    ALTER DATABASE DatabaseName
                                    SET RECOVERY FULL");
    }