Bootstrap

SQL Server日志的自我陈述--数据库日志介绍和管理不完全指南

SQL Server日志的自我陈述--数据库日志介绍和管理不完全指南

作者:Max Shen

听过了人间的悲喜,最难过的莫过于做IT的数据库坏掉了。而更难过的是没有备份,比更难过还难过的是日志不完整。

是时候讲一讲日志的故事。不懂日志的DBA一定是个一个假的DBA,不懂日志备份的工程师一定没经历过系统的大悲大喜。

关键知识点

每个  SQL Server 数据库都具有事务日志,用于记录所有事务以及每个事务对数据库所做的修改。 事务日志是数据库的重要组件,如果系统出现故障,则可能需要使用事务日志将数据库恢复到一致状态。

很多工程师对于数据库日志总觉得没有用,甚至不保留日志, 对于这样的工程师来说,只能说上帝保佑你吧。

SQL Server 事务日志按逻辑运行,就好像事务日志是一串日志记录一样。 每条日志记录由一个日志序列号 (LSN) 标识。 每条新日志记录均写入日志的逻辑结尾处,并使用一个比前面记录的 LSN 更高的 LSN。 日志记录按创建时的串行序列存储。 每条日志记录都包含其所属事务的 ID。 对于每个事务,与事务相关联的所有日志记录通过使用可提高事务回滚速度的向后指针挨个链接在一个链中。  

日志包含操作

许多类型的操作都记录在事务日志中。 这些操作包括:  

  • 每个事务的开始和结束。 

  • 每次数据修改(插入、更新或删除)。 这包括系统存储过程或数据定义语言 (DDL) 语句对包括系统表在内的任何表所做的更改。 

  • 每次分配或释放区和页。 

  • 创建或删除表或索引。 

    回滚操作也记录在日志中。 每个事务都在事务日志中保留空间,以确保存在足够的日志空间来支持由显式回滚语句或遇到错误引起的回滚。 保留的空间量取决于在事务中执行的操作,但通常等于用于记录每个操作的空间量。 事务完成后将释放此保留空间。 

    在很多时候发现数据没有响应,有没有任何的用户操作,但是数据库处理工作状态,包括进行分离后附加也有不行,这样的情况可能就是日志在进行回滚,做了什么大型的操作导致,所以在数据库运维的一条规则,建议对数据库的大数据集操作进行分片化

日志物理体系结构

数据库中的事务日志映射在一个或多个物理文件上。 从概念上讲,日志文件是一系列日志记录。 从物理上讲,日志记录序列被有效地存储在实现事务日志的物理文件集中。 每个数据库必须至少有一个日志文件。  

SQL Server 数据库引擎 在内部将每物理日志文件分成多个虚拟日志文件 (VLF)。 虚拟日志文件没有固定大小,且物理日志文件所包含的虚拟日志文件数不固定。 数据库引擎 在创建或扩展日志文件时动态选择虚拟日志文件的大小。 数据库引擎 尝试维护少量的虚拟文件。 在扩展日志文件后,虚拟文件的大小是现有日志大小和新文件增量大小之和。 管理员不能配置或设置虚拟日志文件的大小或数量。

虚拟日志文件 (VLF) 的创建遵循此方法:

  • 如果下一次增长少于当前日志物理大小的 1/8,则创建 1 个 VLF,补偿此增长大小(从  SQL Server 2014 (12.x) 开始)

  • 如果下一次增长超过当前日志大小的 1/8,则使用 pre-2014 方法:

    • 如果增长少于 64 MB,创建 4 个 VLF,补偿此增长大小(如增长 1 MB,创建四个 256KB 的 VLF)

    • 如果增长在 64 MB 到 1GB 之间,创建 8 个 VLF,补偿此增长大小(如增长 512 MB,创建八个 64MB 的 VLF)

    • 如果增长大于 1GB,创建 16 个 VLF,补偿此增长大小(如增长 8 GB,创建十六个 512MB VLF)

如果这些日志文件由于许多微小增量而增长到很大,则它们将具有很多虚拟日志文件。 这会降低数据库启动以及日志备份和还原操作的速度。 相反,如果日志文件设置得较大,但只有少量或仅一个增量,则它们将只有几个非常大的虚拟日志文件。

建议为日志文件分配一个接近于最终所需大小的 size 值,使用所需增量实现最佳 VLF 分发,并且还要分配一个相对较大的 自动增长值。 

工作机制

事务日志是一种回绕的文件。 例如,假设有一个数据库,它包含一个分成四个 VLF 的物理日志文件。 当创建数据库时,逻辑日志文件从物理日志文件的始端开始。 新日志记录被添加到逻辑日志的末端,然后向物理日志的末端扩张。 日志截断将释放记录全部在最小恢复日志序列号 (MinLSN) 之前出现的所有虚拟日志。 MinLSN 是成功进行数据库范围内回滚所需的最早日志记录的日志序列号。 如下图所示相似。


当逻辑日志的末端到达物理日志文件的末端时,新的日志记录将回绕到物理日志文件的始端。



这个循环不断重复,只要逻辑日志的末端不到达逻辑日志的始端。 如果经常截断旧的日志记录,始终为到下一个检查点前创建的所有新日志记录保留足够的空间,则日志永远不会填满。 但是,如果逻辑日志的末端真的到达了逻辑日志的始端,将发生以下两种情况之一: 

如果对日志启用了 FILEGROWTH 设置且磁盘上有可用空间,则文件就按 growth_increment 参数指定的数量增大,并且新的日志记录将添加到增大的空间中。

  • 如果未启用 FILEGROWTH 设置,或保存日志文件的磁盘的可用空间比 growth_increment 中指定的数量少,则会出现 9002 错误。

    如果日志包含多个物理日志文件,则逻辑日志在回绕到首个物理日志文件始端之前,将沿着所有物理日志文件移动。

活动日志

日志文件中从必须存在以确保数据库范围内成功回滚的第一条日志记录到最后写入的日志记录之间的部分称为日志的活动部分,即“活动日志”。 这是进行数据库完整恢复所需的日志部分。

永远不能截断活动日志的任何部分。这就是很多人说日志不能截断的原因。

此第一条日志记录的日志序列号 (LSN),称为最小恢复 LSN (MinLSN)。

日志文件中从 MinLSN 到最后写入的日志记录这一部分称为日志的活动部分,或者称为活动日志。 这是进行数据库完整恢复所需的日志部分。 永远不能截断活动日志的任何部分。 所有的日志记录都必须从 MinLSN 之前的日志部分截断。

下图显示了具有两个活动事务的结束事务日志的简化版本。 检查点记录已压缩成单个记录。

LSN 148 是事务日志中的最后一条记录。 在处理 LSN 147 处记录的检查点时,Tran 1 已经提交,而 Tran 2 是唯一的活动事务。 这就使 Tran 2 的第一条日志记录成为执行最后一个检查点时处于活动状态的事务的最旧日志记录。 这使 LSN 142(Tran 2 的开始事务记录)成为 MinLSN。

活动日志必须包括所有未提交事务的每一部分。 如果应用程序开始执行一个事务但未提交或回滚,将会阻止数据库引擎推进 MinLSN。 这可能会导致两种问题:

  • 如果系统在事务执行了许多未提交的修改后关闭,以后重新启动时,恢复阶段所用的时间将比“恢复间隔”选项指定的时间长得多。

  • 因为不能截断 MinLSN 之后的日志部分,日志可能变得很大。 即使数据库使用的是简单恢复模式,这种情况也有可能出现,在简单恢复模式下,每次执行自动检查点操作时通常都会截断事务日志。

复制事务

日志读取器代理监视已为事务复制配置的每个数据库的事务日志,并将已设复制标记的事务从事务日志复制到分发数据库中。 活动日志必须包含标记为要复制但尚未传递给分发数据库的所有事务。 如果不及时复制这些事务,它们可能会阻止截断日志

数据库检查点

检查点将脏数据页从当前数据库的缓冲区高速缓存刷新到磁盘上。 这最大限度地减少了数据库完整恢复时必须处理的活动日志部分。 在完整恢复时,需执行下列操作:

  • 前滚系统停止之前尚未刷新到磁盘上的日志记录修改信息。

  • 回滚与未完成的事务(如没有 COMMIT 或 ROLLBACK 日志记录的事务)相关联的所有修改。

    检查点的操作

    检查点在数据库中执行下列过程:

    • 将记录写入标记检查点起点的日志文件。

    • 将为检查点记录的信息存储在检查点日志记录链内。 

        检查点中记录的一条信息是第一条日志记录的日志序列号 (LSN),该 LSN 必须存在才能进行成功的数据库范围的回滚。 该 LSN 称为“最小恢复 LSN”(“MinLSN”)。 MinLSN 是下列各项中的最小者:

      • 检查点起点的 LSN。

      • 最早的活动事务起点的 LSN。

      • 尚未传递给分发数据库的最早的复制事务起点的 LSN。

        检查点记录还包含所有已修改数据库的活动事务的列表。

    • 如果数据库使用简单恢复模式,则标记在 MinLSN 前重用的空间。

    • 将所有脏日志和数据页写入磁盘。

    • 将标记检查点结束的记录写入日志文件。

    • 将这条链起点的 LSN 写入数据库引导页。

下列情况下将出现检查点:

  • 显式执行 CHECKPOINT 语句。 用于连接的当前数据库中出现检查点。

  • 在数据库中执行了最小日志记录操作,例如,在使用大容量日志恢复模式的数据库中执行大容量复制操作。

  • 已经使用 ALTER DATABASE 添加或删除了数据库文件。

  • 通过 SHUTDOWN 语句或通过停止 SQL Server (MSSQLSERVER) 服务停止了 SQL Server 实例。 任一操作都会在 SQL Server 实例的每个数据库中生成一个检查点。

  • SQL Server 实例在每个数据库内定期生成自动检查点,以减少实例恢复数据库所需的时间。

  • 进行了数据库备份。

  • 执行了需要关闭数据库的活动。 例如,AUTO_CLOSE 设置为 ON 并且关闭了数据库的最后一个用户连接,或者执行了需要重新启动数据库的数据库选项更改。

自动检查点

SQL Server 数据库引擎生成自动检查点。 自动检查点之间的间隔基于使用的日志空间量以及自上一个检查点以来经历的时间。 如果只在数据库中进行了很少的修改,自动检查点之间的时间间隔可能变化很大并且很长。 如果修改了大量数据,自动检查点也会经常出现。

使用“恢复间隔”服务器配置选项为服务器实例上的所有数据库计算自动检查点之间的间隔。 此选项指定数据库引擎在系统重新启动时恢复数据库所用的最长时间。 数据库引擎将估计在执行恢复操作期间自己在“恢复间隔”内能够处理多少条日志记录。 

自动检查点之间的间隔也取决于恢复模式:

  • 如果数据库使用的是完整恢复模式或批量日志恢复模式,则每当日志记录数达到数据库引擎估计在“恢复间隔”选项中指定的时间内可以处理的数量时,便会生成一个自动检查点。

  • 如果数据库使用的是简单恢复模式,只要日志记录数达到下面两个值中较小的那个值,就会生成自动检查点:

    • 日志已满 70%。

    • 日志记录数达到数据库引擎估计在“恢复间隔”选项指定的时间内能够处理的记录数。


    • 说了 这么多其实检查点不用管,基本上1分钟会自己做一次

最佳实践

控制日志大小

日志的变化会非常影响系统,曾经碰到过客户数据文件只有5G,日志文件超过300G,这是绝对不正常的。 因此我们需要对日志进行监控,管理,控制,其中重要的就是控制日志大小。

监控日志

使用 select * from sys.dm_db_log_space_usage  可以查看到日志空间情况。

select * from sys.database_files  可以看到日志的配置情况。

管理tempdb日志

tempdb 如果过大,最好的处理方式是 :重新启动服务器实例可以将 tempdb 数据库的事务日志调整到自动增长之前的原始大小。但是原始大小如果太小,  这会降低 tempdb 事务日志的性能,因此可以设置一个较为合理的大小数值。

收缩日志

DBCC SHRINKFILE 可以收缩日志,但是日志没有截断,收缩日志不会有效果。日志可以设置为自动收缩,不建议这样做。

建议

建议为日志文件分配一个接近于最终所需大小的 size 值,使用所需增量实现最佳 VLF 分发,并且还要分配一个相对较大的自动增长值。

建议增长值设置为256M-512M 之间

日志截断

日志截断主要用于阻止日志填充。 日志截断从 SQL Server 数据库的逻辑事务日志中删除不活动的虚拟日志文件,释放逻辑日志中的空间以便物理事务日志重用这些空间。 如果事务日志从不截断,它最终将填满分配给物理日志文件的所有磁盘空间。 但是,在截断日志前,必须执行检查点操作。 检查点将当前内存中已修改的页(称为“脏页”)和事务日志信息从内存写入磁盘。 执行检查点时,事务日志的不活动部分将标记为可重用。 此后,日志截断可以释放不活动的部分。

如何控制事务持续性

您作为 DBA,可以控制用户是否可通过以下语句对数据库使用延迟事务持续性。 您必须使用 ALTER DATABASE 来设置延迟持续性设置。


ALTER DATABASE … SET DELAYED_DURABILITY = { DISABLED | ALLOWED | FORCED }    

DISABLED  [默认] 使用此设置时,不管提交级别设置如何 (DELAYED_DURABILITY=[ON | OFF]),对数据库提交的所有事务都是完全持久事务。 无需更改和重新编译存储过程。 这样能确保任何数据都不会因延迟持续性面临风险。   

ALLOWED  使用此设置时,每个事务的持续性都在事务级别确定 - DELAYED_DURABILITY = { OFF | ON }。 

FORCED  使用此设置,对数据库提交的每个事务都是延迟持久事务。 无论事务指定完全持久 (DELAYED_DURABILITY = OFF) 还是不进行任何指定,事务都是延迟持久事务。 当数据库适合使用延迟事务持续性,并且您不希望更改任何应用程序代码时,此设置很有用。

使用DISABLED 在大写入的时候会对性能有影响。所以而已采取 ALLOWED允许延迟写入,这样就可以提高性能。 面临的风险是:

发生灾难性事件(如服务器崩溃)时,将丢失已提交但未保存到磁盘的所有事务的数据。 根据数据库中的任何表(持久内存优化或基于磁盘)执行完全持久的事务时,或调用 sp_flush_log 时,延迟的持久事务保存到磁盘。 如果你在使用延迟的持久事务,那么你可能想要在数据库中创建一个小型表,你可定期更新该表或调用 sp_flush_log ,以保存所有未完成的已提交事务。 事务日志还会在变满时刷新,但这难以预测,也无法进行控制。   

  对于延迟的持久性,  SQL Server的意外关闭和预期关闭/重新启动没有区别。 与灾难性事件类似,应制定针对数据丢失的计划。 在进行计划的关闭/重新启动时,一些尚未写入磁盘的事务可能会首先保存到磁盘,但不应对其进行计划。 虽然计划了关闭/重启,但无论是否计划,都会像灾难性事件一样丢失数据。

关于恢复模式

SQL Server有三种恢复模式,参加下表

恢复模式Description工作丢失的风险能否恢复到时点?
Simple无日志备份。   自动回收日志空间以减少空间需求,实际上不再需要管理事务日志空间。 简单恢复模式不支持要求事务日志备份的操作。 在简单恢复模式中不能使用以下功能:   -日志传送   -AlwaysOn 或数据库镜像   -没有数据丢失的介质恢复   -时点还原最新备份之后的更改不受保护。 在发生灾难时,这些更改必须重做。只能恢复到备份的结尾。
Full需要日志备份。   数据文件丢失或损坏不会导致丢失工作。   可以恢复到任意时点(例如应用程序或用户错误之前)。 正常情况下没有。   如果日志尾部损坏,则必须重做自最新日志备份之后所做的更改。如果备份在接近特定的时点完成,则可以恢复到该时点。
大容量日志需要日志备份。   是完整恢复模式的附加模式,允许执行高性能的大容量复制操作。   通过使用最小方式记录大多数大容量操作,减少日志空间使用量。 如果在最新日志备份后发生日志损坏或执行大容量日志记录操作,则必须重做自该上次备份之后所做的更改。   否则不丢失任何工作。可以恢复到任何备份的结尾。 不支持时点恢复。

根据上表,所有重要的业务系统,除了测试和开发环境,我强烈建议大家使用完整恢复模式

日志备份

在创建任何日志备份之前,您必须至少创建一个完整备份。 然后,可以随时备份事务日志,除非已备份此日志。

建议经常执行日志备份,这样既可尽量减少丢失工作的风险,也可以截断事务日志。 

数据库管理员通常偶尔(如每周)会创建完整数据库备份,还可以选择以较短间隔(如每天)创建一系列差异备份。 与数据库备份无关,数据库管理员可以比较频繁地创建事务日志备份。 对于给定的备份类型,最恰当的备份间隔取决于一系列因素,如数据的重要性、数据库的大小和服务器的工作负荷。 有关实现好策略的详细信息

例如可以这样做:

Time事件
上午 8:00备份数据库。
中午12:00备份事务日志。
下午 4:00备份事务日志。
晚上 6:00备份数据库。
晚上 8:00备份事务日志。

日志备份建议

  • 在备份链里面如果任何事务日志损坏,则最新有效备份以后执行的工作将丢失。 因此,我们强烈建议您将日志文件存储在容错的存储设备中。

  • 如果数据库已损坏,或者你要还原数据库,建议你创建一个 结尾日志备份 ,使你可以将数据库还原到当前时间点。

  • 默认情况下,每个成功的备份操作都会在 SQL Server 错误日志和系统事件日志中添加一个条目。 如果非常频繁地备份日志,这些成功消息会迅速累积,从而产生一个巨大的错误日志,这样会使查找其他消息变得非常困难。 在这些情况下,如果任何脚本均不依赖于这些日志条目,则可以使用跟踪标志 3226 取消这些条目。

  • 请经常进行日志备份,其频率应足够支持业务需求,尤其是对损坏的日志存储可能导致的数据丢失的容忍程度。

  • 适当的日志备份频率取决于您对工作丢失风险的容忍程度与所能存储、管理和潜在还原的日志备份数量之间的平衡。 实现恢复策略时,请考虑必需的 RTO 和 RPO,特别是日志备份频率。

  • 每 15 到 30 分钟进行一次日志备份可能就已足够。 但是如果您的业务要求将工作丢失的风险最小化,请考虑进行更频繁的日志备份。 频繁的日志备份还有增加日志截断频率的优点,其结果是日志文件较小。

总结

说了这么多,大多都是抄的 Docs,重要的事情是有3件

1、数据库一定使用完整恢复模式

2、一定要备份数据库日志

3、数据库日志一定要监视和管理

;