正如大家所知道的那样,数据库里不仅有数据,还有日志,便是Log。我们知道,数据库的数据终究是依赖于系统的IO写入到文件里去并从文件读取的。数据库不能保证系统能成功的执行它的每一个写出的请求,大家知道,系统也不容易,天知道什么时候就断电了,UPS也不是常常工作的,硬盘可能停止工作了,主板可能烧掉,内存甚至会爆掉。于是呢,数据库说,求人不如求己,我自己保证我的数据永远都是对的。
于是它用了LOG。每次数据库进行修改时,相应的动作都回保存下来,在这些修改写入数据库之前,LOG先写到硬盘上去。每个数据库启动的时候,检查一下LOG,看它的状态是否和实际数据一致。
当然在99.999%的情况下都是一致的,在剩下的情况下,LOG是有用的。
LOG是怎样记录的呢?
LOG包含一系列的LOG Record,这些记录包含一个递增的序列号,LSN。每个LOG Record包含一个事务的ID,所以不同事务的LOG Record能够共存。每个LOG有一个起始位置,在这个位置后是最小的LSN,新的LOG record依次排在后面,直到到达文件结尾。这时,新的Log Record会插入到LOG的开始位置,当然假设之前LOG Record已经被释放掉了。如果释放的Log record不足以满足新的需求,就需要增加log文件的大小。如果LOG文件的大小被限制了,操作就会失败。
LOG中的哪些部分用于数据恢复呢?
正如你所预料的那样,并非LOG中所有的记录都用于数据库的恢复。那些所关联的事务已经提交的LOG记录不会影响到数据恢复的。那些事务已经提交,但数据没被写入硬盘的LOG记录;以及事务没有提交,但因为检查点之类的原因写到硬盘的; 都会被用于数据的恢复,前者称为前滚,后者称为后滚。
数据库是如何使用LOG进行数据恢复的呢?
整个恢复过程分成3个步骤:
分析:找到所有SQL Server停止时没有提交或回滚的事务,存在表中,active Transaction Table;找到最后一个检查点后的发生了写操作的页面,存在表中, Dirty Page Table;
前滚:找到active transaction table事务中所有事务,从最老的一个开始,找到其开始的LSN,依次获取锁,找到最后一个检查点,再对之后的LOG记录分别做指定的修改操作。对各个事务分别处理。
后滚:现在各个活动事务的所有最后一个检查点后的修改都已经写入到硬盘上了。可是这些事务事实上都没有提交,需要回滚。这时,数据库会根据LOG记录做反修改记录,然后再找到它之前的LOG记录,直到遍历事务的所有记录。
下图显示了这一过程:
从上面的恢复过程我们可以看到,日志记录中有用的只是关于未完成事务的部分,因此为了将LOG保持在一个合理的大小,我们应该尽量避免使用巨大的事务。
同时我们也应该注意到LOG文件并不会自动的缩短,即使所有事务都已经结束,它的空间还是不会释放。事实上每个LOG文件分成一组虚拟LOG文件,每个虚拟LOG处于以下四种状态之一:
Active: 从活动事务的最小LSN开始的所有LOG记录
Recoverable: 在所有活动事务之前的部分
Reusable:不再需要的部分
Unused:还未被用到的部分
每次使用dbcc shrinkdatabase的时候,都会有一个或几个非活动的虚拟日志被删除掉,从而腾出空间。
您可以通过DBCC LOGINFO来查看数据库种的虚拟日志。
你可以通过以下的方式设置自动缩短日志文件:
1. Backup Log with no_log 或者 Backup Log with Truncate_only
2. 将恢复模式设为simple
3. 另外如果你从未做过full数据库备份的话,虚拟日志文件也会自动减少;
在这写状态下,数据库会在日志文件足够大的时候自动删除空闲的虚拟日志文件。足够大是指数据库启动的恢复时间不会太长,通常在10M以内。
关于日志的管理,以后会谈到。