为什么事务日志不断增长或空间不足?

Mik*_*lsh 283 sql-server shrink transaction-log auto-growth recovery-model

这个问题在大多数论坛和整个网络中似乎是一个常见问题,这里以多种格式提出,通常听起来像这样:

在 SQL Server 中 -

  • 事务日志变得如此之大的一些原因是什么?
  • 为什么我的日志文件这么大?
  • 有什么方法可以防止这个问题的发生?
  • 当我找到根本原因并希望将我的事务日志文件调整到正常大小时,我该怎么办?

Mik*_*lsh 348

更简短的答案:

您可能有一个长时间运行的事务正在运行(索引维护?大批量删除或更新?),或者您处于“默认”(更多关于默认含义的下面)恢复模式Full并且没有进行日志备份(或服用它们的频率不够高)。

如果是恢复模式问题,Simple如果您不需要时间点恢复和定期日志备份,简单的答案可能是切换到恢复模式。但是,许多人在不了解恢复模型的情况下就做出了回答。请继续阅读以了解其重要性,然后决定您要做什么。您也可以开始进行日志备份并保持Full恢复状态。

可能还有其他原因,但这些是最常见的。这个答案开始深入探讨最常见的两个原因,并为您提供一些关于原因背后的原因和方式的背景信息,并探讨了其他一些原因。


更长的答案: 什么场景会导致日志不断增长?原因有很多,但通常这些原因有以下两种模式: 对恢复模型有误解或存在长时间运行的事务。请继续阅读以了解详细信息。

首要原因 1/2:不了解恢复模型

处于完全恢复模式并且不进行日志备份- 这是最常见的原因 - 遇到此问题的绝大多数是。

虽然这个答案不是对 SQL Server 恢复模型的深入探讨,但恢复模型的主题对这个问题至关重要。

在 SQL Server 中,有三种恢复模型

  • Full,
  • Bulk-Logged
  • Simple.

我们暂时忽略Bulk-Logged,我们会说它是一个混合模型,大多数使用这种模型的人都是有原因的,并且了解恢复模型。

我们关心的两个及其混淆是大多数人遇到此问题的原因是SimpleFull

中场休息:一般恢复

在我们谈论恢复模型之前:让我们谈谈一般的恢复。如果您想更深入地了解这个主题,只需阅读Paul Randal 的博客以及您想要的尽可能多的帖子。不过对于这个问题:

  1. 崩溃/重启恢复
    事务日志文件的一个目的是用于崩溃/重启恢复。用于前滚和回滚在崩溃或重新启动之前已完成的工作(前滚/重做)以及在崩溃或重新启动后已启动但未完成的工作(回滚/撤消)。事务日志的工作是查看事务已启动但从未完成(在事务提交之前发生回滚或崩溃/重新启动)。在这种情况下,日志的工作是在恢复期间说“嘿.. 这从未真正完成,让我们回滚它”。查看您确实完成了某些事情并且您的客户端应用程序被告知它已完成(即使它尚未固化到您的数据文件中)也是日志的工作,然后说“嘿.. 这真的发生了,让我们向前推进,让它像应用程序认为的那样”在重新启动后。现在还有更多,但这是主要目的。

  2. 时间点恢复
    事务日志文件的另一个目的是使我们能够恢复到由于数据库中的“oops”而导致的时间点,或者在发生硬件故障时保证恢复点涉及数据库的数据和/或日志文件。如果此事务日志包含为恢复而启动和完成的事务的记录,则 SQL Server 可以并且确实使用此信息将数据库恢复到问题发生前的位置。但这对我们来说并不总是可用的选择。为此,我们必须将数据库置于正确的恢复模式中,并且必须进行日志备份

恢复模型

进入恢复模型:

  • 简单的恢复模型
    所以有了上面的介绍,最容易先谈谈Simple Recovery模型。在此模型中,您告诉 SQL Server:“我对您使用事务日志文件进行崩溃和重新启动恢复感到满意……”(您确实别无选择。查找ACID 属性,这应该很快就有意义了。)“...但是一旦您不再需要它来用于崩溃/重启恢复目的,请继续并重用日志文件。”

    SQL Server 在 Simple Recovery 中侦听此请求,并且仅保留执行崩溃/重新启动恢复所需的信息。一旦 SQL Server 确定它可以恢复,因为数据已加固到数据文件(或多或少),已加固的数据在日志中不再需要并被标记为截断 - 这意味着它会被重新使用。

  • 完全恢复模型
    使用Full Recovery,您告诉 SQL Server 您希望能够恢复到特定时间点,只要您的日志文件可用或恢复到日志备份涵盖的特定时间点。在这种情况下,当 SQL Server 达到在简单恢复模型中截断日志文件是安全的点时,它不会这样做。相反,它让日志文件继续增长,并允许它继续增长,直到您在正常情况下进行日志备份(或日志文件驱动器上的空间不足)。

从简单到完整切换有一个陷阱。

这里有规则和例外。我们将在下面深入讨论长时间运行的事务。

但是对于完全恢复模式要记住的一个警告是:如果您只是切换到Full Recovery模式,但从不进行初始完全备份,SQL Server 将不会满足您进入Full Recovery模型的请求。您的事务日志将继续按原样运行,Simple直到您切换到完整恢复模式并执行第一个Full Backup.

没有日志备份的完整恢复模式很糟糕。

那么,日志增长不受控制的最常见原因是什么?答:处于完全恢复模式,没有任何日志备份。

这种情况发生的所有的时间的人。

为什么这是一个常见的错误?

为什么它总是发生?因为每个新数据库通过查看模型数据库来获取其初始恢复模型设置。

模型的初始恢复模型设置始终为Full Recovery Model- 直到并且除非有人更改该设置。所以你可以说“默认恢复模式”是Full. 许多人没有意识到这一点,他们的数据库在Full Recovery Model没有日志备份的情况下运行,因此事务日志文件比必要的要大得多。这就是为什么当默认值不适合您的组织及其需求时更改默认值很重要的原因)

日志备份太少的完整恢复模型很糟糕。

您也可能因为没有足够频繁地进行日志备份而陷入困境。
每天进行一次日志备份听起来不错,它使恢复所需的恢复命令更少,但请记住上面的讨论,该日志文件将继续增长,直到您进行日志备份为止。

如何找出我需要的日志备份频率?

您需要牢记两件事来考虑日志备份频率:

  1. 恢复需求- 这应该是第一个。如果您的事务日志所在的驱动器坏了,或者您的日志备份受到严重损坏,那么会丢失多少数据?如果该数字不超过 10-15 分钟,那么您需要每 10-15 分钟进行一次日志备份,讨论结束。
  2. 日志增长- 如果您的组织可以轻松地重新创建当天的数据,因此可以丢失更多数据,那么您可以将日志备份频率降低到 15 分钟以内。也许你的组织每 4 小时就没事。但是你必须看看你在 4 小时内产生了多少交易。允许日志在这四个小时内不断增长会导致日志文件过大吗?这是否意味着您的日志备份需要太长时间?

首要原因 2/2:长时间运行的事务

“我的恢复模式很好!日志还在增长!

这也可能是导致不受控制和不受限制的日志增长的原因。无论恢复模式如何,但它经常出现“但我处于简单恢复模式 - 为什么我的日志仍在增长?!”

这里的原因很简单:如果 SQL 像我上面描述的那样使用这个事务日志用于恢复目的,那么它必须回到事务的开始。

如果您的事务需要很长时间或进行大量更改,则日志无法在检查点上截断任何仍在打开的事务中或自该事务启动后已启动的更改的检查点。

这意味着在一个 delete 语句中删除数百万行的大删除是一个事务,并且在整个删除完成之前,日志不能进行任何截断。在 中Full Recovery Model,此删除已被记录,并且可能是大量日志记录。与维护窗口期间的索引优化工作相同。这也意味着糟糕的事务管理以及不注意和关闭打开的事务会真正伤害您和您的日志文件。

我可以对这些长时间运行的事务做些什么?

您可以通过以下方式自救:

  • 适当调整日志文件的大小以应对最坏的情况 - 例如您的维护或已知的大型操作。当您增加日志文件时,您应该查看Kimberly Tripp 的这份指南(以及她发送给您的两个链接)。正确的尺寸在这里非常重要。
  • 观察您的交易使用情况。不要在您的应用程序服务器中启动事务并开始与 SQL Server 进行长时间的对话,并冒着让一个打开的时间过长的风险。
  • 观察DML 语句中隐含的事务。例如:UPDATE TableName Set Col1 = 'New Value'是一个交易。我没有把它放在BEGIN TRAN那里,我也不必,它仍然是一个在完成后自动提交的事务。因此,如果对大量行进行操作,请考虑将这些操作批处理成更易于管理的块,并为日志提供恢复时间。或者考虑合适的尺寸来解决这个问题。或者可能会考虑在批量加载窗口期间更改恢复模型。

这两个原因是否也适用于日志传送?

简短的回答:是的。下面有更长的答案。

问题:“我使用日志传送,所以我的日志备份是自动的......为什么我仍然看到事务日志增长?”

答案:继续阅读。

什么是日志传送?

日志传送就像听起来一样 - 您将事务日志备份传送到另一台服务器以用于 DR 目的。有一些初始化,但之后的过程相当简单:

  • 在一台服务器上备份日志的工作,
  • 复制该日志备份的作业和
  • 在目标服务器上无需恢复(NORECOVERYSTANDBY)即可恢复它的作业。

如果事情没有按照您的计划进行,还有一些工作需要监控和提醒。

在某些情况下,您可能只想每天或每三天或每周执行一次日志传送还原。那没关系。但是,如果您对所有作业(包括日志备份和复制作业)进行此更改,则意味着您一直在等待进行日志备份。这意味着您将有大量的日志增长——因为您处于没有日志备份的完全恢复模式——这也可能意味着要复制一个大的日志文件。您应该只修改还原作业的计划,并让日志备份和复制更频繁地发生,否则您将遇到此答案中描述的第一个问题。


通过状态代码进行一般故障排除

除了这两个原因之外,还有其他原因,但这些是最常见的。无论原因如何:有一种方法可以分析这种无法解释的日志增长/缺乏截断的原因,并查看它们是什么。

通过查询sys.databases目录视图,您可以看到描述日志文件可能正在等待截断/重用的原因的信息。

有一列log_reuse_wait使用原因代码的查找 ID调用,一log_reuse_wait_desc列包含等待原因的描述。来自参考书籍的在线文章是大多数原因(您可能会看到的原因以及我们可以解释原因的原因。缺少的要么已停止使用,要么供内部使用)以及有关等待的一些注意事项斜体

  • 0 = 没有
    听起来像什么.. 不应该等待

  • 1 = 检查点
    等待检查点发生。这应该发生并且您应该没问题-但是有一些情况需要在这里查找以获取以后的答案或编辑。

  • 2 = 日志备份
    您正在等待日志备份发生。要么你安排了它们,很快就会发生,要么你有这里描述的第一个问题,你现在知道如何解决它

  • 3 = 活动备份或还原
    正在数据库上运行备份或还原操作

  • 4 = 活动事务
    有一个活动事务需要完成(无论哪种方式 -ROLLBACKCOMMIT),然后才能备份日志。这是此答案中描述的第二个原因。

  • 5 = 数据库镜像
    在高性能镜像情况下,镜像落后或处于某些延迟状态,或者镜像由于某种原因暂停

  • 6 = 复制
    复制可能存在导致这种情况的问题 - 例如日志读取器代理未运行、数据库认为它已标记为不再进行复制以及各种其他原因。您也可以看到这个原因,这是完全正常的,因为您正在查看正确的时间,就像日志阅读器正在消耗事务一样

  • 7 = 数据库快照创建
    您正在创建一个数据库快照,如果您在创建快照时查看正确的时刻,您将看到这一点

  • 8 = 日志扫描
    我还没有遇到过这个永远运行的问题。如果您看的时间足够长且足够频繁,您可以看到这种情况发生,但这不应是我见过的事务日志过度增长的原因。

  • 9 = AlwaysOn 可用性组辅助副本正在将此数据库的事务日志记录应用到相应的辅助数据库。 关于最清晰的描述。。

  • 页面拆分将增加日志记录。一个重要的原因(根据我的经验)没有提到可能需要频繁收缩的大增长,这在我的很多情况下已经解决了,那就是使用适当的索引选择,包括适当的 FillFactor mgmt。我使用以下设置,仔细观察。FF 设置:(0/100) 高读取/低写入的表,(90) 稍作修改,(80) 中读取/低中写入,(70) 高写入,(60) 我几乎没有达到这个水平或其他可能是错误的。然后正确使用索引管理计划匹配数据量。 (2认同)

Aar*_*and 123

由于我对 Stack Overflow 上的任何答案都不满意,包括投票最多的建议,并且因为有一些我想解决 Mike 的答案没有解决的问题,我想我会提供我的意见也在这里。我也在那里放了一份这个答案的副本。

缩小日志文件确实应该保留用于遇到意外增长而您不希望再次发生的情况。如果日志文件将再次增长到相同的大小,则暂时缩小它并不能完成很多工作。现在,根据数据库的恢复目标,这些是您应该采取的操作。

首先,进行完整备份

永远不要对您的数据库进行任何更改,除非确保您可以在出现问题时将其恢复。

如果您关心时间点恢复

(通过时间点恢复,我的意思是您关心能够恢复到完整备份或差异备份以外的任何内容。)

大概您的数据库处于FULL恢复模式。如果不是,请确保它是:

ALTER DATABASE yourdb SET RECOVERY FULL;
Run Code Online (Sandbox Code Playgroud)

即使您正在定期进行完整备份,日志文件也会不断增长,直到您执行日志备份 - 这是为了您的保护,而不是不必要地消耗您的磁盘空间。根据您的恢复目标,您应该经常执行这些日志备份。例如,如果您有一个业务规则,规定在发生灾难时您可以承受至少 15 分钟的数据丢失,那么您应该有一个每 15 分钟备份一次日志的作业。这是一个脚本,它将根据当前时间生成带时间戳的文件名(但您也可以使用维护计划等来执行此操作,只是不要在维护计划中选择任何收缩选项,它们很糟糕)。

DECLARE @path NVARCHAR(255) = N'\\backup_share\log\yourdb_' 
  + CONVERT(CHAR(8), GETDATE(), 112) + '_'
  + REPLACE(CONVERT(CHAR(8), GETDATE(), 108),':','')
  + '.trn';

BACKUP LOG foo TO DISK = @path WITH INIT, COMPRESSION;
Run Code Online (Sandbox Code Playgroud)

请注意,\\backup_share\应该在代表不同底层存储设备的不同机器上。将这些备份到同一台机器(或使用相同底层磁盘的不同机器,或同一物理主机上的不同虚拟机)并没有真正帮助你,因为如果机器爆炸,你已经丢失了你的数据库它的备份。根据您的网络基础设施,在本地备份然后将它们转移到幕后的不同位置可能更有意义;无论哪种情况,您都希望尽快将它们从主数据库机器上删除。

现在,一旦您运行了常规的日志备份,就应该合理地将日志文件缩小到比它现在被炸毁的更合理的地方。但这并不意味着运行SHRINKFILE了一遍又一遍,直到日志文件为1 MB -即使你经常备份日志,它仍然需要适应可能出现的任何并发事务的总和。日志文件自动增长事件代价高昂,因为 SQL Server 必须将文件归零(与启用即时文件初始化时的数据文件不同),并且用户事务必须在发生这种情况时等待。您希望尽可能少地执行这种增长-收缩-增长-收缩的例程,并且您当然不想让您的用户为此付出代价。

请注意,在可能进行收缩之前,您可能需要备份日志两次(感谢 Robert)。

因此,您需要为日志文件提供一个实用的大小。这里没有人可以在不了解更多有关您的系统的情况下告诉您这是什么,但是如果您经常缩小日志文件并且它再次增长,那么好的水印可能比最大的水印高 10-50% . 假设达到 200 MB,并且您希望任何后续自动增长事件为 50 MB,那么您可以通过以下方式调整日志文件大小:

USE [master];
GO
ALTER DATABASE Test1 
  MODIFY FILE
  (NAME = yourdb_log, SIZE = 200MB, FILEGROWTH = 50MB);
GO
Run Code Online (Sandbox Code Playgroud)

请注意,如果日志文件当前 > 200 MB,您可能需要先运行:

USE yourdb;
GO
DBCC SHRINKFILE(yourdb_log, 200);
GO
Run Code Online (Sandbox Code Playgroud)

如果您不关心时间点恢复

如果这是一个测试数据库,并且您不关心时间点恢复,那么您应该确保您的数据库处于SIMPLE恢复模式。

ALTER DATABASE yourdb SET RECOVERY SIMPLE;
Run Code Online (Sandbox Code Playgroud)

将数据库置于SIMPLE恢复模式将确保 SQL Server 重新使用日志文件的一部分(基本上逐步淘汰非活动事务),而不是增长以保留所有事务的记录(就像FULL在备份日志之前恢复所做的那样)。CHECKPOINTevents 将有助于控制日志并确保它不需要增长,除非您在CHECKPOINTs之间生成大量 t-log 活动。

接下来,您应该绝对确保这种日志增长确实是由于异常事件(例如,年度春季大扫除或重建您最大的索引),而不是由于正常的日常使用。如果您将日志文件缩小到小得离谱,而 SQL Server 只需要再次增大它以适应您的正常活动,您得到了什么?您是否能够使用您只是暂时释放的磁盘空间?如果您需要立即修复,则可以运行以下命令:

USE yourdb;
GO
CHECKPOINT;
GO
CHECKPOINT; -- run twice to ensure file wrap-around
GO
-- 200 MB
DBCC SHRINKFILE(yourdb_log, 200);
GO
Run Code Online (Sandbox Code Playgroud)

否则,设置适当的大小和增长率。根据时间点恢复案例中的示例,您可以使用相同的代码和逻辑来确定合适的文件大小并设置合理的自动增长参数。

有些事情你不想做

  • 使用TRUNCATE_ONLY选项备份日志,然后使用SHRINKFILE。一方面,此TRUNCATE_ONLY选项已被弃用,并且在当前版本的 SQL Server 中不再可用。其次,如果您处于FULL恢复模式,这将破坏您的日志链并需要一个新的完整备份。

  • 分离数据库,删除日志文件,然后重新附加. 我无法强调这有多危险。您的数据库可能无法备份,可能会被怀疑,您可能必须恢复备份(如果有),等等。

  • 使用“收缩数据库”选项DBCC SHRINKDATABASE和维护计划选项做同样的事情是坏主意,特别是如果你真的只需要解决日志问题。使用DBCC SHRINKFILEALTER DATABASE ... MODIFY FILE(上面的示例)定位要调整的文件并独立调整它。

  • 将日志文件缩小到 1 MB。这看起来很诱人,因为,嘿,SQL Server 会让我在某些情况下这样做,看看它释放的所有空间!除非您的数据库是只读的(并且是这样,您应该使用 将其标记为只读ALTER DATABASE),否则这绝对会导致许多不必要的增长事件,因为无论恢复模式如何,日志都必须容纳当前事务。暂时释放该空间有什么意义,只是为了让 SQL Server 可以缓慢而痛苦地收回它?

  • 创建第二个日志文件。这将为已填满磁盘的驱动器提供暂时的缓解,但这就像试图用创可贴修复被刺破的肺。您应该直接处理有问题的日志文件,而不是仅仅添加另一个潜在问题。除了将某些事务日志活动重定向到不同的驱动器之外,第二个日志文件对您实际上没有任何作用(与第二个数据文件不同),因为一次只能使用其中一个文件。Paul Randal 还解释了为什么多个日志文件会在以后咬你

主动

与其将您的日志文件缩小到很小的数量并让它自己以很小的速度不断自动增长,不如将其设置为一个相当大的大小(可以容纳最大的并发事务集的总和)并设置一个合理的自动增长设置为后备,这样它就不必多次增长来满足单个事务,因此在正常业务运营期间它必须增长的情况相对较少。

此处最糟糕的设置是 1 MB 增长或 10% 增长。有趣的是,这些是 SQL Server 的默认设置(我曾抱怨过并要求更改无济于事)——数据文件为 1 MB,日志文件为 10%。前者在当今时代太小了,而后者每次都会导致越来越长的事件(例如,您的日志文件是 500 MB,第一次增长是 50 MB,下一次增长是 55 MB,下一次增长是 60.5 MB等 - 以及在慢速 I/O 上,相信我,您会真正注意到这条曲线)。

进一步阅读

请不要停在这里;虽然您在那里看到的许多关于缩小日志文件的建议本质上都是糟糕的,甚至可能是灾难性的,但有些人更关心数据完整性而不是释放磁盘空间。


小智 30

您还可以查看日志文件的内容。为此,您可以使用未记录的fn_dblog或事务日志阅读器,例如ApexSQL Log

它不显示索引重组,但它显示了所有DML和各种DDL事件:ALTERCREATEDROP,触发启用/禁用,授予/撤销权限,对象重命名。

ApexSQLLogProject.temp - ApexSQL.log

免责声明:我作为支持工程师为 ApexSQL 工作


Ram*_*chi 8

这是日志增长并填满磁盘的几乎所有 DBA 最常面临的问题。

• 事务日志增长如此之大的原因是什么?

  1. 长活动交易
  2. 高日志记录事务,如索引重建、重新组织、批量插入、删除等。
  3. 任何 HA,如复制、镜像配置,它保存日志并且不允许它释放日志空间

• 为什么我的日志文件这么大?

检查表中的log_reuse_wait_desc 列sys.databases以了解截断日志的原因:

select name, log_reuse_wait_desc 
from sys.databases
Run Code Online (Sandbox Code Playgroud)

• 有哪些方法可以防止此问题的发生?

日志备份将帮助您控制日志增长,除非存在阻止日志被重用的因素。

• 当我找到根本原因并希望将我的事务日志文件调整到正常大小时,我该怎么办?

如果您已经确定是什么导致了它,那么请尝试按照下页中的说明进行相应的修复。

https://www.brentozar.com/archive/2016/03/my-favorite-system-column-log_reuse_wait_desc/

除非出现异常情况,否则安排适当的日志备份是处理日志增长的最佳方式。