为什么完整备份不能截断事务日志

9/1/2015来源:SQL技巧人气:1637

为什么完整备份不能截断事务日志

导言

完整备份不能截断事务日志,这是所有SQL Server DBA的一个常识,

为此,当数据库处于完整恢复模式时(非特别说明,下文所提到都是完整恢复模式下的数据库),DBA们必须频繁地使用事务日志备份的方式来防止日志文件变得过大。

这几乎成为了DBA们的一个定理,但,作为一个DBA,你证明过这个定理吗?你知道为什么完整备份不能截断事务日志吗?

一个错误的”常识“

将一个完整备份还原到新数据库时,新数据库无论是mdf还是ldf,其大小都跟原始数据库一模一样,

以至于我们认为完整备份包括了mdf中所有数据和ldf中的所有事务日志。这几乎成为了一些DBA的”常识“。

正是如此,我们有”理由“认为:数据库在完整备份后,ldf中的事务日志应该被截断,没必要再保存一个副本。

让“常识”站不住脚

按照这个”常识“,既然完整备份中包含了原始数据库中所有的事务日志,那通过完整备份还原得到的新数据库应该也含有同样的事务日志。下面我们通过一个小实验来验证实际情况是否是这样。

验证思路:

--首先新建一个数据库和一张表,并将数据库设置完整恢复模式下

CREATE DATABASE test; USE Test; CREATE TABLE t1(col1 INT,col2 VARCHAR(25));ALTER DATABASE Test SET RECOVERY FULL;

--在表中插入5条数据 

INSERT INTO t1 VALUES(1,'chen1'); INSERT INTO t1 VALUES(2,'chen2'); INSERT INTO t1 VALUES(3,'chen3'); INSERT INTO t1 VALUES(4,'chen4'); INSERT INTO t1 VALUES(5,'chen5');

--完整备份数据库 
BACKUP DATABASE Test TO DISK='d:\temp\oldtest.bak'
--还原到新数据库 
RESTORE DATABASE test_new FROM DISK='d:\temp\oldtest.bak' WITH MOVE 'test' TO 'd:\temp\test.mdf', MOVE 'test_log' TO 'd:\temp\test_log.ldf' --对比新老数据库LDF中的事务日志大小 DBCC SQLPERF(LOGSPACE)
--结果:LDF文件大小相同
7_thumb2
--对比新老数据库LDF中的事务日志内容(通过ApexSQL Log软件分析新老数据库的LDF文件)
--结果:原数据库和新数据库的事务日志不相同
6_thumb2

结论1:

通过完整备份文件还原得到的数据库,尽管其LDF的大小与原数据库相同,但两者所包含的事务日志并不完全相同,如上例所述,原数据库test包含5条insert的事务日志,而新数据库test_new没有这些事务日志。如果完整备份截断了事务日志,则无论是原数据库的LDF文件还是完整备份文件,都将不包含事务日志,这无异于将数据库置于简单恢复模式下,显然不符合我们将数据库设置成完整恢复模式的初衷。

完整备份的那些事

通过上述实例我们已经有了足够的理由推翻那个错误的“常识”,但,这个例子似乎并不完美,因为它又将我们导向了另一个错误的极端——完整备份不包括任何事务日志。

真是这样的吗?

本质是是现象的根源,只有真正了解了数据库完整备份期间发生的那些事,我们才能揭开层层迷雾。

归纳起来,数据库的完整备份主要包括如下几个步骤:

    1. 执行Checkpoint,并标记此时数据库中事务日志的LSN

    2. 开始读取、拷贝data files中的数据

    3. 结束data files的读取和拷贝,并记录此时最后一个活动日志的起始LSN

    4. 读取并拷贝必需的日志文件

摘自:http://technet.microsoft.com/en-us/magazine/2009.07.sqlbackup.aspx#id0980008

8_thumb3

备注:读者在查看上述日志时,请先开启3004、3605、3502这三个跟踪标记。

DBCC TRACEON(3004,3605,3502,-1)

至此,我们已经明白了,在完整备份文件里,它虽然没有包含所有的事务日志,但在完整备份期间,拷贝日志的动作一直存在,按照SQL Server的说法,完整备份选择性的保存了数据库必需的事务日志。

为什么要选择性的保存这些必需事务日志呢?什么是“必需的事务日志”?而我的样例中新数据库为何没有任何事务日志?

在回答这些问题前,我们先看一下MSDN对完整备份文件的一个描述:

Full database backups rePResent the database at the time the backup finished.

也就是说,完整备份文件能够呈现数据库在备份完成时间点的所有数据,那么,这些足够多的事务日志的目的肯定是为了将数据库还原到备份完成时的时间点。

有了这个概念,我们来看一个完整备份的场景,下图绿色的数字代表完整备份的每个步骤:

  1. 执行事务A
  2. 执行完整备份和checkpoint,开始读取和拷贝数据文件
  3. 备份进程读取数据页X
  4. 执行事务B,改变数据页X的数据
  5. 结束事务B
  6. 结束对数据文件的读取和拷贝

9_thumb1

数据库的完整备份从时间点2持续到时间点7。

对于页面 X,在时间点3做了备份后,在时间点5被事务B做了修改,因此,此时备份文件中页面X的数据已经过期了、不准确,而备份进程不会回过头来重新读取页面X。因为“完整备份文件必须呈现数据库在备份完成时间点的所有数据”,所以,数据库必须备份事务B的日志,以便在数据库通过重做此日志还原到时间点7的状态。

对于事务A,其执行的时间早于备份的开始时间,且在数据文件备份期间一直没有结束,是这次完整备份的活动事务。

尽管事务A没有提交,但时间点2的checkpoint已将事务A所做的修改写入到数据文件中,如果完整备份文件没有包含事务A的日志,则这些未提交的修改将无法得到回滚,导致数据不一致的情况发生。

综上所述:

结论2

如果在完整备份前不存在活动事务,则必需的事务日志范围:从备份开始时的LSN到备份结束时的LSN;

如果在完整备份前存在活动事务,则必需的事务日志范围:从最早活动事务开始的LSN到备份结束时的LSN;

10_thumb2

在我之前的样例中,因为5条insert语句在数据库备份前已经执行完成并提交了,所以备份文件不会保存这些日志,因而在还原得到的新数据库中看不到这些日志。

至此,我们已经完成了这个定理的证明过程,感谢各位的耐心品读,欢迎大家批评指教。