SQL Server收缩数据文件和日志文件
Tags: MSSQLSQL Server基础维护收缩数据文件收缩日志脚本
简介
事务日志记录着在相关数据库上的操作,同时还存储数据库恢复(recovery)的相关信息。
收缩日志的原因有很多种,有些是考虑空间不足,有些则是应用程序限制导致的。
SQL Server的full和simple之间是什么区别
SQL Server 中的“full”和“simple”是指数据库恢复模型,这两种模型决定了数据库如何进行日志记录和恢复。下面是它们之间的区别:
- Simple模型:在简单恢复模式下,SQL Server仅保留当前活动事务的日志信息,一旦这些事务提交,相应的日志记录将被清除。这意味着,如果出现故障,只能将整个数据库还原到最近备份的状态,无法使用全量备份+日志追加恢复到最新时刻。简单恢复模式适用于仅需要在备份之间进行最小的数据丢失的数据库。
- Full模型:在完整恢复模式下,SQL Server将保留所有事务的日志信息,包括已提交和未提交的事务。这允许数据库管理员使用日志备份文件逐步恢复数据库到发生故障之前的某个时间点。完整恢复模式适用于要求最小数据丢失和最小停机时间的数据库。
总之,简单恢复模式提供了更少的恢复选项,但通常比完整恢复模式需要更少的磁盘空间,而完整恢复模式可以提供更高级的恢复选项,但需要更多的磁盘空间。因此,应该根据数据库的需求和负担来选择适当的恢复模式。
SQL Server收缩数据文件需要注意什么
在 SQL Server 中,当数据库的数据文件(.mdf)中存在大量未使用的空间时,可以使用收缩操作将文件大小减小,从而节省磁盘空间。但是,在执行收缩操作时需要注意以下几点:
- 收缩操作可能导致性能下降:收缩操作会移动数据文件中的数据,如果数据文件很大,则此操作可能需要很长时间,从而导致性能下降。因此,在执行此操作之前,应该在非高峰期执行,以避免影响生产环境。
- 收缩操作可能导致索引碎片化:收缩操作会导致数据文件中的数据移动,因此可能导致表和索引的碎片化,从而影响性能。因此,在执行此操作之前,应该考虑使用索引重新组织或重建来修复任何可能的碎片化。
- 收缩操作可能不会显著减少文件大小:在某些情况下,收缩操作可能不会显著减少数据文件的大小。例如,如果数据文件中存在大量未使用的空间,但是该空间包含有数据页,那么在收缩操作后,文件大小可能只会略微减小。
- 收缩操作可能会造成数据损坏:在收缩操作期间,可能会发生磁盘故障、系统故障或其他异常情况,这可能导致数据损坏。因此,在执行此操作之前,应该备份所有数据,并确保备份的完整性。
总之,在执行收缩操作之前,应该了解其可能产生的影响和潜在风险,并在执行此操作之前备份所有数据。同时,应该在执行此操作之前,评估数据文件的大小和结构,以确保收缩操作可以产生预期的效果。
数据文件收缩后文件大小不变的原因
在 SQL Server 中,数据文件的收缩操作(使用DBCC SHRINKDATABASE或DBCC SHRINKFILE命令)旨在减小数据库文件的物理大小,从而释放未使用的空间,以节省磁盘空间。然而,在执行此操作后,文件大小可能没有显著减小,或者仍然与收缩操作之前的大小相同,原因可能有以下几个方面:
- 未使用的空间比较分散:如果数据库中存在许多未使用的空间,但是这些空间在文件中分散,那么收缩操作可能无法将它们整理在一起,以便将其释放。这意味着在执行收缩操作后,文件大小可能不会显著减小。
- 日志文件过大:如果事务日志文件(.ldf)过大,它可能会占用磁盘空间并阻止数据文件缩小。在这种情况下,可以使用DBCC SHRINKFILE命令来减小事务日志文件的大小。
- 数据文件的碎片化:如果数据文件中存在大量的碎片化,那么在执行收缩操作之前,可以考虑使用索引重组或重建等操作来修复数据文件中的碎片化。这样,收缩操作就可以更有效地减小文件大小。
- 自动增长设置:如果自动增长设置过大,那么即使收缩操作成功将文件减小到最小大小,文件大小也会随着自动增长操作而增加。因此,可以调整自动增长设置来减小文件的增长速度。
- 文件空间限制:如果数据文件已达到文件系统的空间限制,那么即使收缩操作成功,文件大小也不会变化。
总之,收缩数据文件操作的效果取决于许多因素,包括数据库的大小、碎片化程度、日志文件的大小和自动增长设置等。在执行此操作之前,应该评估这些因素,并采取适当的步骤来减小文件的大小。
如果未使用的空间在数据文件中比较分散,可能需要采用一些特殊的方法才能彻底收缩数据库。以下是一些可能的方法:
- 重建所有索引:使用DBCC DBREINDEX或ALTER INDEX命令可以重建数据库中的所有索引。这可以消除索引碎片,使数据在磁盘上更加连续,从而有助于收缩数据库。
- 迁移数据库到新的文件组:如果数据库中的表和索引已经很大,可以考虑将它们迁移到一个新的文件组中,然后删除旧的文件组。在此过程中,可以选择调整文件组的大小和配置,以更好地满足数据库的需求。
- 导出和导入数据:如果数据库中只有少量数据需要保存,可以考虑将其导出到一个文件中,然后重新创建一个新的数据库并将数据导入到其中。这可以消除所有未使用的空间,并使数据库更加紧凑。
无论使用哪种方法,重要的是在执行此类操作之前,确保对数据库进行备份并测试所有更改,以确保数据完整性和可用性。
DBCC SHRINKDATABASE 和 DBCC SHRINKFILE命令的区别有哪些
DBCC SHRINKDATABASE和DBCC SHRINKFILE命令都是在SQL Server中用于压缩数据库或日志文件的命令,但它们之间有以下区别:
- 范围不同:DBCC SHRINKDATABASE命令可以压缩整个数据库,包括所有的数据和日志文件,而DBCC SHRINKFILE命令只能压缩一个指定的数据或日志文件。
- 语法不同:DBCC SHRINKDATABASE命令使用数据库名称作为参数,而DBCC SHRINKFILE命令使用文件名称和大小作为参数。
- 对数据和日志文件的处理方式不同:DBCC SHRINKDATABASE命令将压缩整个数据库,包括所有的数据和日志文件,而DBCC SHRINKFILE命令可以将日志文件或数据文件压缩到指定的大小。
- 压缩效率不同:DBCC SHRINKFILE命令的压缩效率通常比DBCC SHRINKDATABASE命令高,因为DBCC SHRINKFILE只需要压缩单个文件,而DBCC SHRINKDATABASE需要同时处理多个文件。
- 可能引起性能问题:DBCC SHRINKDATABASE和DBCC SHRINKFILE命令都可能引起性能问题。特别是在执行这些命令时,系统可能会出现阻塞和性能下降。因此,在执行这些命令之前,应该谨慎评估其影响并备份数据库。
总之,DBCC SHRINKDATABASE和DBCC SHRINKFILE命令都可以用于压缩数据库或日志文件,但它们的使用方式、范围和效率不同。在使用这些命令时,需要考虑其影响并选择合适的命令来满足需求。
1 2 3 4 | DBCC SHRINKDATABASE(N'lhrdb'); DBCC SHRINKFILE(N'lhrdb' , 0, TRUNCATEONLY) ; |
收缩数据文件和日志文件的方法
方法1:使用命令
通过修改恢复模式为“简单”,这种收缩日志的方法是不得以的方法,也是终极方法,在收缩之前,在完整模式下,进行备份;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | -- 备份数据库和日志 BACKUP DATABASE [lhrdb] TO DISK = N'D:\MSSQL_backup\lhrdb_20200927_OGG_START.bak' WITH NOFORMAT, NOINIT, NAME = N'lhrdb_20200927_OGG_START', SKIP, REWIND, NOUNLOAD,compression, STATS = 10; GO BACKUP LOG [lhrdb] TO DISK = N'D:\MSSQL_backup\lhrdb_20200927_OGG_START_LOG.bak' ; GO -- 切换数据库 USE lhrdb -- 设置为simple模式后,查询log_reuse_wait_desc必须返回NOTHING才可以正常回收日志 SELECT name,log_reuse_wait_desc FROM sys.databases where name='lhrdb' ; GO -- 查询日志文件和数据文件的名称 SELECT type_desc,NAME, size FROM sys.database_files ; GO ALTER DATABASE lhrdb SET RECOVERY SIMPLE WITH NO_WAIT ; GO ALTER DATABASE lhrdb SET RECOVERY SIMPLE ; GO USE lhrdb -- 若收缩日志文件,则这里的lhrdb_log为日志文件名称 DBCC SHRINKFILE(N'lhrdb_log' , 0, TRUNCATEONLY) ; -- 若收缩数据文件,则这里的lhrdb为数据文件名称 -- DBCC SHRINKFILE(N'lhrdb' , 0, TRUNCATEONLY) ; GO ALTER DATABASE lhrdb SET RECOVERY FULL WITH NO_WAIT ; GO ALTER DATABASE lhrdb SET RECOVERY FULL ; GO -- 备份数据库 BACKUP DATABASE [lhrdb] TO DISK = N'D:\MSSQL_backup\lhrdb_20200927_OGG_STOP.bak' WITH NOFORMAT, NOINIT, NAME = N'lhrdb_20200927_OGG_STOP', SKIP, REWIND, NOUNLOAD, compression, STATS = 10; GO |
方法2:通过图形界面逐步操作
下面介绍的是在简单模式下,进行收缩操作。
1、打开数据库属性窗口
2、更改数据库恢复模式,“完整”改成“简单”
3、收缩数据库日志,“任务”->“收缩”
4、收缩完,将数据库的恢复模式修改为“完整”
日志收缩示例
如果数据库的恢复模式为FULL(完整恢复模式),那么在数据库运行过程中会产生很多事务日志,若不清理,长年累月日志就会很大,如下的巡检输出结果:
日志有300多g,而其实数据库才30多G,所以需要收缩,收缩过程参考方法1。
这里需要说明的一点是:日志有300g,若是执行收缩(
DBCC SHRINKFILE('test_Log' , 1
)操作,其实也就是几秒钟的时间,所以不用担心。
总结
1、收缩日志主要使用DBCC SHRINKFILE(N'lhrdb_log' , 0,TRUNCATEONLY) ;
,用于收缩当前数据库的指定数据文件或日志文件大小。
2、收缩日志文件和数据文件的方法是一样的。
3、无论日志多大,收缩日志是很快的;对于数据文件也是很快的,但是,在很大可能下,收缩后,数据文件大小并不会变化。除非重组数据文件。
4、数据文件收缩的100%进度可以通过 sys.dm_exec_requests 的字段 percent_complete 来看
5、数据文件收缩很容易出现等待,收缩会话对应 sys.sysprocesses 的字段 waitresource 值类似为15:1:4700649,sys.sysprocesses 的字段 lastwaittype 值为PAGEIOLATCH_SH或PAGEIOLATCH_EX等
6、不建议使用DBCC SHRINKDATABASE
,因为太慢了。。。
7、查询数据库的数据文件大小和真实的数据库大小:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | -- 当前数据库真实大小 SELECT CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS TotalSpaceMB, CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS UsedSpaceMB, CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) AS UnusedSpaceMB FROM sys.tables t INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id ; /***********************************************************/ /***************************** 数据库真实大小 **********************/ /***********************************************************/ DECLARE @database_name VARCHAR(50) DECLARE @SQL_STR VARCHAR(2000) IF OBJECT_ID('tempdb..#TB_DB_SIZES') IS NOT NULL DROP TABLE #TB_DB_SIZES CREATE TABLE #TB_DB_SIZES(db_name nvarchar(200),totalspacemb numeric(36, 2),usedspacemb numeric(36, 2),unusedspacemb numeric(36, 2)) DECLARE DATEBASE_INFO_CURSOR CURSOR FOR SELECT name FROM sys.databases where state=0 -- where name not in ('master','model','msdb','tempdb') -- and state=0 ORDER BY Name OPEN DATEBASE_INFO_CURSOR FETCH NEXT FROM DATEBASE_INFO_CURSOR INTO @database_name WHILE @@FETCH_STATUS=0 BEGIN SET @SQL_STR='INSERT INTO #TB_DB_SIZES SELECT '''+@database_name+''', CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS totalspacemb, CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS usedspacemb, CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) AS unusedspacemb FROM ['+@database_name+'].sys.tables t INNER JOIN ['+@database_name+'].sys.indexes i ON t.OBJECT_ID = i.object_id INNER JOIN ['+@database_name+'].sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id INNER JOIN ['+@database_name+'].sys.allocation_units a ON p.partition_id = a.container_id LEFT OUTER JOIN ['+@database_name+'].sys.schemas s ON t.schema_id = s.schema_id ' -- print (@SQL_STR) EXEC (@SQL_STR) FETCH NEXT FROM DATEBASE_INFO_CURSOR INTO @database_name END CLOSE DATEBASE_INFO_CURSOR DEALLOCATE DATEBASE_INFO_CURSOR select * from #TB_DB_SIZES order by totalspacemb desc ; /***********************************************************/ /***************************** 数据文件大小 **********************/ /***********************************************************/ -- 数据文件大小 SELECT cast(a.database_id as varchar(10)) AS database_id, a.name AS name, convert(varchar(20),a.create_date,120) AS create_date, a.recovery_model_desc AS recovery_model_desc, ISNULL(a.collation_name,' ') AS collation_name, a.user_access_desc AS user_access_desc, a.state_desc AS state_desc, a.is_auto_create_stats_on AS is_auto_create_stats_on, a.is_auto_update_stats_on AS is_auto_update_stats_on, a.is_auto_close_on AS is_auto_close_on, a.is_auto_shrink_on AS is_auto_shrink_on, a.is_auto_update_stats_async_on AS is_auto_update_stats_async_on, a.compatibility_level AS compatibility_level, a.log_reuse_wait_desc AS log_reuse_wait_desc, a.page_verify_option_desc AS page_verify_option_desc, a.is_cdc_enabled as is_cdc_enabled, (SELECT 'is_replication' = CASE WHEN b.category = 1 THEN 'Published' WHEN b.category = 2 THEN 'subscribed' WHEN b.category = 4 THEN 'Merge published' WHEN b.category = 8 THEN 'merge subscribed' Else 'NO replication' END) AS is_replication, ISNULL(c.mirroring_state,' ') as mirroring_state , (select cast(round(sum(size), 2) as numeric(15, 2)) from (select database_id, type, size * 8.0 / 1024 size from sys.master_files) fs where type = 0 and fs.database_id = a.database_id) AS '数据文件大小(MB)', (select cast(round(sum(size), 2) as numeric(15, 2)) from (select database_id, type, size * 8.0 / 1024 size from sys.master_files) fs where type = 1 and fs.database_id = a.database_id) AS '日志大小(MB)', (select cast(round(sum(size), 2) as numeric(15, 2)) from (select database_id, type, size * 8.0 / 1024 size from sys.master_files) fs where fs.database_id = a.database_id) AS '数据库大小(MB)' -- Exec sp_spaceused from sys.databases as a LEFT JOIN sys.sysdatabases b ON a.database_id=b.dbid LEFT JOIN sys.database_mirroring c ON a.database_id=c.database_id where a.name!='tempdb' UNION ALL SELECT '总计','','','','','','','','','','','','','','','','','', (select cast(round(sum(size * 8.0 / 1024), 2) as numeric(15, 2)) size from sys.master_files WHERE type=0) , (select cast(round(sum(size * 8.0 / 1024), 2) as numeric(15, 2)) size from sys.master_files WHERE type=1) , (select cast(round(sum(size * 8.0 / 1024), 2) as numeric(15, 2)) size from sys.master_files) ; |