SQL Server 事物日志空间收缩

数据库存储空间使用率是我们运维中需要必不可少的一个资源监控点,甚至说它的监控要比CPU、内存、IO使用率还要重要。当SQL Server实例磁盘使用率达到高水位时,我们首先需要判断究竟是哪部分空间的空间占用最大,然后再进行对应的措施考虑是否可以释放空闲空间,本章考虑的是当SQL Server事务日志空间使用率较高时,我们应该如何排查定位以及处理。

1 定位空间瓶颈点

1.1 OS层面

操作系统层面,可以直接查各个磁盘目录文件大小定位磁盘空间消耗最大数据库文件。

1.2 数据库层面

通过数据库查询系统表定位数据库实例磁盘消耗最大文件

SELECT DB_NAME(database_id) AS [Database Name],
       [Name] AS [Logical Name],
       [Physical_Name] AS [Physical Name],
       ((size * 8) / 1024) AS [Size(MB)]
FROM sys.master_files
ORDER BY [Size(MB)] DESC

2 事务日志空间收缩

2.1 事务日志备份收缩

我们空间瓶颈或者异常的定位,我们可以具体的定位到是哪一数据库的事务日志空间存在问题,所以接下来我们需要查看该数据库的事务日志状态,查看事务日志是否可以直接进行收缩

SELECT [name],[recovery_model_desc],
       [log_reuse_wait_desc]
FROM master.sys.databases

数据库恢复模式区别:

  • SIMPLE : 简单模式下,当数据库进行checkpoint时会自动对事物日志进行截断,该模式下事物日志一般不会太大
  • FULL : 完整恢复模式下,需要通过事物日志备份对事物日志进行截断,否则事物日志空间会不断增长

需要重点关注log_reuse_wait_desc的状态值:

  • NOTHING :目前存在一个或者多个可复用的虚拟日志文件(VLF),事务日志在当前状态下是可以进行收缩的
  • CHECKPOINT : 自上一次日志截断后还未发生checkpoint或是事务日志使用还未超过一个VLF,也是事务日志长时间未被截断的原因之一
  • LOG_BACKUP : 需要进行一次事务日志备份后,才可进行事务日志截断(Full or Bulk-logged recover model),当进行第二次事务日志备份后,事务日志空间才会成为NOTHING状态
  • ACTIVE_BACKUP_OR_RESTORE :当前数据库正处于数据备份或者数据恢复状态
  • ACTIVE_TRANSACTION : 数据库中存在活跃事务导致无法进行事务日志截断,长事物不仅可能会导致当前数据库事务日志空间的增长,也可能会导致tempdb数据库事务日志空间暴涨
  • DATABASE_MIRRORING : 数据库镜像被中断或者有较大延迟

更多状态以及状态解释可参考:https://docs.microsoft.com/en-us/sql/relational-databases/logs/the-transaction-log-sql-server?view=sql-server-ver15

不同的log_reuse_wait_desc状态下,我们对应的处理方式不一致:

1、若log_reuse_wait_desc为ACTIVE_TRANSACTION,表示当前数据库有活跃事务一直未提交。我们需要优先处理活跃事务,才能进行后续的事务日志备份进行日志截断、最终进行事务日志空间收缩操作

1)定位活跃事务,可重点关注sys.dm_tran_database_transactions表信息

-- 查看执行时间超过10分钟的活跃事务
SELECT  ST.session_id,ST.transaction_id AS TransactionID ,  
        DB_NAME(DT.database_id) AS DatabaseName ,  
        AT.transaction_begin_time AS TransactionStartTime ,  
        DATEDIFF(minute, AT.transaction_begin_time, GETDATE()) AS Tran_run_time ,  
        CASE AT.transaction_type  
          WHEN 1 THEN 'Read/Write Transaction'  
          WHEN 2 THEN 'Read-Only Transaction'  
          WHEN 3 THEN 'System Transaction'  
          WHEN 4 THEN 'Distributed Transaction'  
        END AS TransactionType ,  
        CASE AT.transaction_state  
          WHEN 0 THEN 'Transaction Not Initialized'  
          WHEN 1 THEN 'Transaction Initialized & Not Started'  
          WHEN 2 THEN 'Active Transaction'  
          WHEN 3 THEN 'Transaction Ended'  
          WHEN 4 THEN 'Distributed Transaction Initiated Commit Process'  
          WHEN 5 THEN 'Transaction in Prepared State & Waiting Resolution'  
          WHEN 6 THEN 'Transaction Committed'  
          WHEN 7 THEN 'Transaction Rolling Back'  
          WHEN 8 THEN 'Transaction Rolled Back'  
        END AS TransactionState  
FROM    sys.dm_tran_session_transactions AS ST  
        INNER JOIN sys.dm_tran_active_transactions AS AT ON ST.transaction_id = AT.transaction_id  
        INNER JOIN sys.dm_tran_database_transactions AS DT ON ST.transaction_id = DT.transaction_id  
WHERE DATEDIFF(minute, AT.transaction_begin_time, GETDATE())>10 -- 找出运行时间大于10分钟的事务
ORDER BY TransactionStartTime  

-- 获取长时间未完成的活跃事务SPID
DBCC OPENTRAN

-- 查看对应SPID信息
select * from sys.sysprocesses where spid=${SPID}


2)将活动事务进行kill

DBCC INPUTBUFFER(${SPID})

3)将数据库活跃事务进行kill完毕后,事务日志状态可正常转变为LOG_BACKUP,LOG_BACKUP状态下的处理方式具体参考如下

2、若log_reuse_wait_desc为LOG_BACKUP,表示事务日志未进行日志备份,需要进行事务日志备份后才可进行截断、事务日志空间收缩的操作

1)若数据库事务日志从未进行过备份,那么必须进行两次事务日志备份后,事务日志才可被截断,且状态转变成为NOTHING,此时事务日志被可正常进行数据日志收缩。相关参考文档连接为:https://docs.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002?view=sql-server-2017

SQL Server 事物日志空间收缩_第1张图片

2)若数据库事务日志之前有过备份,那么可直接对当前事务日志进行备份,备份后事务日志截断且状态转变为NOTHING,表示事物已经被截断完成

--事物日志备份命令
BACKUP LOG [dbname] TO DISK = 'some_volume:\some_folder\dbname_LOG.trn'

3)检查数据库事物日志状态与使用率

-- 检查数据库文件空间使用率
SELECT a.name [文件名称] ,cast(a.[size]*1.0/128 as decimal(12,1)) AS [文件设置大小(MB)] ,
    CAST( fileproperty(s.name,'SpaceUsed')/(8*16.0) AS DECIMAL(12,1)) AS [文件所占空间(MB)] ,
    CAST( (fileproperty(s.name,'SpaceUsed')/(8*16.0))/(s.size/(8*16.0))*100.0 AS DECIMAL(12,1)) AS [所占空间率%] ,
    CASE WHEN A.growth =0 THEN '文件大小固定,不会增长' ELSE '文件将自动增长' end [增长模式] ,CASE WHEN A.growth > 0 AND is_percent_growth = 0 
    THEN '增量为固定大小' WHEN A.growth > 0 AND is_percent_growth = 1 THEN '增量将用整数百分比表示' ELSE '文件大小固定,不会增长' END AS [增量模式] ,
    CASE WHEN A.growth > 0 AND is_percent_growth = 0 THEN cast(cast(a.growth*1.0/128as decimal(12,0)) AS VARCHAR)+'MB' 
    WHEN A.growth > 0 AND is_percent_growth = 1 THEN cast(cast(a.growth AS decimal(12,0)) AS VARCHAR)+'%' ELSE '文件大小固定,不会增长' end AS [增长值(%或MB)] ,
    a.physical_name AS [文件所在目录] ,a.type_desc AS [文件类型] 
FROM sys.database_files a 
INNER JOIN sys.sysfiles AS s  ON a.[file_id]=s.fileid 
LEFT JOIN sys.dm_db_file_space_usage b ON a.[file_id]=b.[file_id] ORDER BY a.[type]

-- 检查事物日志状态,将要收缩的事物日志状态为NOTHING
SELECT [name],[recovery_model_desc],
       [log_reuse_wait_desc]
FROM master.sys.databases


4)对事物日志进行空间收缩

USE [${DB_Name}]
DBCC SHRINKFILE (N'db1' , 0, TRUNCATEONLY)

2.2 变更数据库恢复模式释放空间

当数据库服务器磁盘空间已经基本打满,没有多余的空间用来进行事物日志备份,那么需要通过变更数据库恢复模式来将数据库日志进行截断,然后进行空间收缩处理。变更数据库恢复模式与上面备份事物日志后处理相对,

1、变更数据库恢复模式进行事物日志收缩流程

1)变更数据库恢复模式,将数据库恢复模式设置为simple模式,该模式下当数据库进行checkpoint时会自动对事物日志进行截断

alter database [$DB_Name] set recovery simple

2)检查事物日志状态以及事物日志空间利用率,确定截断操作完成

-- 检查数据库文件空间使用率
SELECT a.name [文件名称] ,cast(a.[size]*1.0/128 as decimal(12,1)) AS [文件设置大小(MB)] ,
    CAST( fileproperty(s.name,'SpaceUsed')/(8*16.0) AS DECIMAL(12,1)) AS [文件所占空间(MB)] ,
    CAST( (fileproperty(s.name,'SpaceUsed')/(8*16.0))/(s.size/(8*16.0))*100.0 AS DECIMAL(12,1)) AS [所占空间率%] ,
    CASE WHEN A.growth =0 THEN '文件大小固定,不会增长' ELSE '文件将自动增长' end [增长模式] ,CASE WHEN A.growth > 0 AND is_percent_growth = 0 
    THEN '增量为固定大小' WHEN A.growth > 0 AND is_percent_growth = 1 THEN '增量将用整数百分比表示' ELSE '文件大小固定,不会增长' END AS [增量模式] ,
    CASE WHEN A.growth > 0 AND is_percent_growth = 0 THEN cast(cast(a.growth*1.0/128as decimal(12,0)) AS VARCHAR)+'MB' 
    WHEN A.growth > 0 AND is_percent_growth = 1 THEN cast(cast(a.growth AS decimal(12,0)) AS VARCHAR)+'%' ELSE '文件大小固定,不会增长' end AS [增长值(%或MB)] ,
    a.physical_name AS [文件所在目录] ,a.type_desc AS [文件类型] 
FROM sys.database_files a 
INNER JOIN sys.sysfiles AS s  ON a.[file_id]=s.fileid 
LEFT JOIN sys.dm_db_file_space_usage b ON a.[file_id]=b.[file_id] ORDER BY a.[type]

-- 检查事物日志状态,将要收缩的事物日志状态为NOTHING
SELECT [name],[recovery_model_desc],
       [log_reuse_wait_desc]
FROM master.sys.databases

3)变更数据库恢复模式为full

alter database [$DB_Name] set recovery full

2、变更数据库恢复模式进行空间回收的风险

将数据库模式变更为Simple模式,主要是为了快速对数据库事物日志进行截断,当数据库Checkpoint时会自动对数据库事物日志截断。其风险就是该数据库的事物日志丢失,后续将无法对该事物日志包含时间点数据进行按时间点恢复。

该方式下,建议变更完毕后,立即对数据库进行一次全量物理备份,保证变更后时间节点可正常进行数据恢复。

3 预防操作

3.1 事物日志暴涨常见场景

常见的事物日志暴涨的场景如下:

  • 大事物
  • 阻塞事物日志截断的操作
    • FULL恢复模式下,未设置合理的数据库全量备份+事物日志备份
    • 长事物,事物日志状态长时间为ACTIVE_TRANSACTION
    • Checkpoint操作延迟
  • 日志初始值以及日志增长设置不合理,导致事物日志一次性扩展较大空间

3.2 事物日志暴涨预防

为防止数据库事物日志暴涨,我们需要检查如下几点:

  • 完善的数据库备份策略,数据库完整备份+事物日志备份,Full恢复模式下,需要通过事物日志备份来保证数据库事物日志的截断
  • 合理的数据库事物日志设置:初始化大小、自动增长策略
  • 合理的拆分事物,避免长事物、大事物

相关官方文档可参考:https://docs.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002?view=sql-server-ver15

你可能感兴趣的:(SQL,Server,数据库,sqlserver,事物日志)