我有一个 数据库 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分钟后对数据库进行备份,那么备份文件大小为1GB,这样我可以看到未使用的空间已成功清理。换句话说,上面的Sql代码工作正常(XX分钟后的数据库是schrunk)。
问题我需要等到这个查询(收缩操作)完成,所以我尝试执行以下操作:
SELECT percent_complete, start_time, status, command, estimated_completion_time, cpu_time, total_elapsed_time
FROM sys.dm_exec_requests
我在上述查询的结果中找不到有关 SHRINKFILE 命令的任何信息。
我做错了什么吗?为什么我看不到DB收缩操作的进度?
我的主要问题是:我怎样才能等到SHRINKFILE完成? 例如,我可以从 C# 代码查询发送,并在此查询的结果中我将获得 SHRINKFILE 操作是否完成的信息吗?
测量
DBCC SHRINKFILE
进度的问题在于,引擎没有一致的方法来了解缩小文件需要完成多少工作。要理解这一点,就要了解 DBCC SHRINKFILE
是如何工作的。基本上,过程是:
那么为什么这意味着 SQL Server 不知道需要完成多少工作呢?因为它不知道文件中的空白空间有多碎片。如果内容压缩得相当好并且靠近文件的前面,则收缩文件会很快完成。如果没有,可能需要很长时间。好消息是,一旦页面在文件中移动,它们就会被移动。取消收缩文件不会撤消/回滚此工作,因此,如果收缩文件运行了一段时间,然后在其完成之前将其终止,则所有页面移动都保持不变。这意味着您可以在几乎您离开的位置重新启动收缩文件(禁止在文件内创建任何新页面)。
我用不同的方式解决了这个问题,但是这个解决方案不需要任何轮询或等待线程,非常实用。
此方法不会重新组织表索引,而只会从硬盘中删除流文件并回收操作系统的可用空间。
重要的是要知道以下代码将在同一线程上运行;因此,这不会通知您收缩进程的进度,而只会在应用程序线程上运行它。
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");
}