当一切正常时,事务日志会不断地循环,重复使用现有的虚拟日志文件(VLFs)。我将这种行为称为日志的循环特性。然而,有时某些事情会阻止这种情况发生,导致事务日志不断增长,不断添加更多的虚拟日志文件。

VLF状态及日志截断

所有虚拟日志文件(VLFs)都有一个包含有关VLF的元数据的头部结构。在该结构中最重要的字段之一是VLF的状态,我们感兴趣的值是0,意味着VLF处于非活动状态,以及2,意味着VLF处于活动状态。这很重要,因为非活动的VLF可以被重用,但活动的VLF则不能。当VLF种存在被需要的日志记录时,VLF会处于活动状态,以下情况下日志记录会被需要:

  1. 日志是长时间运行的事务的一部分,因此在事务提交或完成回滚之前,它们不能被释放。
  2. 日志备份尚未备份那些日志记录。
  3. 日志读取代理尚未处理事务复制或变更数据捕获的日志部分(CDC)。
  4. 日志的该部分尚未发送到异步数据库镜像或可用性组副本

假设有五个虚拟日志文件(VLFs),并且序列号从1开始(实际上不会从1开始)。当事务日志被创建时,VLF1立即被标记为活动状态,因为在事务日志中始终至少有一个活动VLF——即当前正在写入日志块的VLF。

SQL Server 事务日志体系结构3--事务日志的循环特性_VLF状态及日志截断

随着创建更多的日志记录,更多的日志块被写入事务日志,VLF1被填满,因此VLF2必须变为活动状态以便继续写入更多的日志块:

SQL Server 事务日志体系结构3--事务日志的循环特性_虚拟日志文件_02

SQL Server 跟踪最早未提交(活动)事务的起始点(MinLSN),并且每次发生检查点操作时,这个LSN(日志序列号)都会在磁盘上持久化保存。同时,SQL Server 也会跟踪写入事务日志的最新日志记录的LSN,但它只存储在内存中。

最终,VLF 2会被填满,然后VLF 3将变为活动状态,以此类推。事务日志循环性质的关键在于,事务日志中的早期VLF变成非活动状态,因此可以被重用。这是通过日志截断的过程来完成的,也通常被称为日志清除。这两个术语常常让人误解,因为实际上日志并没有被截断或清除。

日志截断仅仅是检查事务日志中的所有VLF,并确定哪些活动VLF现在可以被标记为非活动状态。当执行日志截断时,并不能保证任何活动VLF可以变为非活动状态——这完全取决于数据库正在发生的情况

SQL Server 事务日志体系结构3--事务日志的循环特性_VLF状态及日志截断_03

有两个错误的观念:

  1. 事务日志会变小(“截断”的误解)。不,它不会——日志截断不会导致大小变化。能够使事务日志变小的唯一方法是显式执行DBCC SHRINKFILE命令。
  2. 非活动VLFs以某种方式被清零。不会——当VLF变为非活动状态时,除了VLF头部的一些字段外,没有内容被写入。

日志截断发生取决于数据库使用的恢复模型:

  1. 简单模式:当检查点操作完成时,日志截断发生。
  2. 完全模式或大容量日志记录模式:当事务日志备份完成时会发生日志截断(只要没有并发的完整或差异备份在运行,在这种情况下,日志截断会被推迟,直到数据备份完成)
日志循环使用

为了避免事务日志需要增长,日志中的第一个物理VLF必须是非活动的,事务日志才能具有其循环特性。

SQL Server 事务日志体系结构3--事务日志的循环特性_虚拟日志文件_04

SQL Server 事务日志体系结构3--事务日志的循环特性_日志循环使用_05

异常情况

当事务日志中的第一个物理VLF不是非活动状态时,事务日志无法循环使用,因此它将会增长(只要它被配置为这样做,并且有足够的磁盘空间)。这通常是因为有某些事情阻止日志截断使VLFs变为非活动状态。如果发现数据库的事务日志正在增长,可以使用以下简单的代码查询SQL Server,以找出是否存在日志截断问题:

SELECT
      [log_reuse_wait_desc]
FROM [master].[sys].[databases]
WHERE [name] = N'MyDatabase';