[翻译]在简单的恢复模式下,为什么日志会持续增长
说明:这篇BLOG的原文标题为:A Race Condition in the Simple Recovery Mode: Why is my log autogrowing。其中,Race Condition 的字面意思为“竞态条件”,感觉不怎么通。但是,文中确确实实是在讲由于SQL SERVER 的其它线程比较繁忙而导致CHECKPOINT线程不能被及时的唤醒和完成工作而导致了日志的增长。翻译水平有限,大家多指教,谢谢。
译文:
“我的数据库工作在简单的恢复模式。不,我没有任何巨大的、长时间运行后者用于复制的事务日志,并且,我有足够数量的VLFs循环使用,准确的说有250个VLFs。为什么我的事务日志会保持持续增长呢?”
正如SQL SERVER 联机丛书所记载的一样,当数据库工作在简单恢复模式的时候,在SQL SEVER唤醒检查点线程并运行的时候,它的事务日志被截断(即:所有的没有包含任何活动事务记录的虚拟日志被标记为可重用),而检查点线程在以下三种情况下会被唤醒,1.恢复的时间间隔阈值被足够多的日志记录超过。2.你手工运行了CHECKPOINT命令。3.事物日志已经达到了70%满。
即使没有任何大型的,长时间运行或者用于复制的事物,对于一个运行在简单恢复模式的数据库来说,当CHECKPOINT进程不能被足够快的唤醒而及时的截断日志,填满日志空间或者在自动增长被启用的情况下导致日志的增长是非常可能发生的事情。
例如:即使更改没有达到恢复间隔的阈值,但是,如果更改在日志70%空间被使用以后非常快,SQL SERVER 在日志空间被用尽以前没有足够的时间来运行CHECKPOINT以完成日志的截断。从某种意义上来说,日志空间是否会被用完(或自动增长)取决于下面两个条件之间的竞争状态:
1. 如果SQL SERVER能够首先截断日志,日志空间将被释放且标记为可重用,那么,将不会自动增长或失败事物。
2. 但是,由于数据改变的足够快以至于在日志被截断以前而用尽了日志的空间,这种情况也是可能发生的。
让我们用一个脚本来实际的阐明这种行为。首先在一个测试数据库中运行一下脚本创建一个测试表并填充一些数据。测试数据库1.已经设置为简单的恢复模式。2.日志的大小固定为100M.3.日志文件的自动增长被禁用(因为观察日志空间被用完的错误比检查自动增长要容易)
go
drop table test
go
create table test(i int, c char(1000))
go
declare @i int
set @i = 1
while @i < 10000
begin
insert test values(@i, 'abc')
set @i = @i + 1
end
为了观察这种资源竞争,运行一下脚本
go
declare @change_size int
set @change_size = 100 -- adjust this value
declare @i int
set @i = 1
while @i < 100
begin
if @i % 2 = 0
update test set c = replicate('a', @change_size)
else
update test set c = replicate('b', @change_size)
select @i = @i + 1
end
你可能需要多次调整变量@change_size的值,首先使用一个比较小的值(比如40)使脚本能够运行至结束,然后挑一个比较大的值(比如400)使该脚本运行失败。请尽量使该脚本在运行中途失败。在我的几次测试中,当@change_size=100时,成功运行了上面的脚本并且在运行过程中日志的空间没有被填满。但是当我将@change_size改为120时,我得到了9002的错误信息非常准确的告诉我数据库的事务日志已满。
当我将变量@change_size设置为116时,有时候脚本会成功结束,而有时候会失败。但这只是一个大致的阈值,在一个不稳定的测试及此测试环境下它是不精确的。实际上,在我的其它一些测试中,这个阈值低至50。这中情况说明,SQL SERVER 在有些时候能够足够快的截断日志,而有时候却不能。
值得注意的是,尽管上面的两个UPDATE语句的每一个都更新了9999行,由修改而占用的日志空间控制的比较好,大约在2-3M或者总日志空间2%-3%.这并不是一个很小的事务,尽管它也不是一个大的,长时间运行的事务。
对于一个工作在简单恢复模式的数据库来说,对我们有什么启示呢?
首先,在事务日志中预留足够的空间。这可能意味着你必须让SQL SERVER 有机会能唤醒CHECKPOINT 进程来及时的截断事务日志。其次,你可能必须手工的发出BACKUP LOG WITH NO_LOG命令来帮助它。
注意,在SQL SERVER 2008中,BACKUP LOG WITH NO_LOG 和BACKUP LOG WITH TRUNCATE_ONLY这两个命令都已经被移除。如果您确实需要手工的截断事务日志,可能您不得不发出CHECKPOINT命令,而这个命令除了截断事务日志以外,还将做很多其它事情。