您的位置:首页 > 数据库 > Oracle

第三章Oracle恢复内部原理(重做日志)

2014-09-18 17:44 337 查看
 


3.1  原子修改

数据库最基础的操作就是以原子的方式修改数据块。前台进程想修改一个或几个数据块时,首先得获取对数据缓冲区中包含该块的缓存的一个排它访问权限。然后构建改变向量。重做日志缓冲区中分配空间保存重做记录。重做缓冲区位于SGA中,LGWR进程定时将重做日志缓冲区中的重做记录写入到重做日志文件中以释放空间。当重做日志满了的时候,LGWR就要做日志切换。注意在重做日志缓冲区中分配空间的同时也会在重做日志文件中分配空间。重做日志缓冲区空间分配后前台进程负责构建重做记录,此后才能修改数据缓冲区中的数据块。然后当重做日志缓冲区中的重做记录写到重做文件中才算数据库变更完成。恢复保证重做日志中记录的变更都会应用到数据文件中(除非是不完全恢复)。

 


3.2  写日志优先

写日志优先是一个缓冲区执行协议,用来协调写脏数据到数据文件中和写重做日志记录到重做日志文件的顺序。根据写日志优先协议,在DBWR进程将脏数据写入到数据文件中之前,LGWR进程必须先将对应的重做日志记录写入到重做日志文件中。

注意,写日志优先协议跟提交时写日志协议是独立的(见3.3)。

同时注意,写日志优先协议只适用于将那些在数据缓冲区中的脏数据写入到数据文件这种情形,不适用于直接路径写(如由直接路径读导致的)。

写日志优先协议保证了数据文件中没有一种变更在重做日志文件中没有记录,不惜以失败为代价。

写日志优先协议还保证了所有数据块都在先写完重做日志并保证能够回滚的情况下才写到磁盘的,使得如果提交失败的时候可以回滚所有修改。这里的重做信息其实就是回滚段的重做信息。

写优先协议在数据库事务层保证事务的原子性起了很大作用。

 


3.3  事务提交

事务提交时会分配一个SCN并且建立一个包含那个SCN提交的重做日志记录。当事务所有的重做记录(包括commit对应的重做记录)都写到磁盘上的重做日志文件中时,commit过程才算结束。因此commit会强制日志刷新到磁盘上——至少截止到commit的重做记录,这就是通常说的log- force-at-commit。

恢复就是设计成这样,在事务commit的时候只需刷新重做记录到重做日志,而不用刷新该事务修改的所有脏数据,为的是在即使失败的情况下也能保证事务持久性。这就是通常说的no-datablock-force-at-commit。

 


3.4  线程检查点事件

线程检查点事件发生时,会将该线程的重做记录中SCN小于指定值的重做记录保护的脏数据都刷新到数据文件中。完成后,该线程在控制文件中的线程检查点结构会被更新。

线程检查点事件开始时,首先是得到一个SCN,初始化一个检查点结构。然后该实例的数据缓冲区中所有脏数据都被打上做检查点的标识。DBWR分阶段将这些标识的脏数据写入到数据文件中。当所有脏数据都写入到数据文件中时,检查点结构中的SCN被更新为前面得到的SCN,然后用该检查点更新该线程在控制文件中的检查点记录。

一个线程检查点事件可能会或者不会推进数据库检查点。当只有一个打开的线程时,新的线程检查点也同时是新的数据库检查点。如果有多个打开的线程,并且当前线程就是数据库检查点所联机程,当前线程的检查点事件会推进数据库检查点。因为新的检查点SCN是最近分配的,很有可能比其他打开状态的线程的检查点SCN要大,数据库检查点SCN将推进到新的最小的线程检查点SCN。不过如果当前线程原有检查点不是数据库检查点,那么该线程的检查点事件不会推进数据库检查点。

数据库检查点推进时,每个数据文件头部的检查点计数也会增长。并且,每个数据文件只要不是在热备份中或者没有更高的检查点SCN(如新增的数据文件或者刚恢复的数据文件),数据文件头部的检查点都会推进到跟新的数据库检查点一致,数据文件头将写入磁盘。同时,数据文件在控制文件中的记录的检查点SCN也会更新为新的数据库检查点SCN。

 


3.5  联机模糊位

你或许已经注意到,在数据缓冲区中还存在一些比那些标识了检查点的脏数据还要新的修改,它们是联机程检查点事件后产生的,因此产生时的SCN要高于数据文件头部的线程检查点SCN。这些脏数据可能因为很多原因也被写到数据文件中。这时,我们称这个数据文件是“联机模糊的”,就是说它包含了一些检查点SCN以后的变更。一个联机的数据文件在数据库打开的时候常常是联机模糊的。

联机模糊状态是通过设置数据文件头部的一个联机模糊位来标识的。所有数据文件的联机模糊位是在数据库打开的时候设置的。另外,一个脱机的数据文件在解除脱机状态时也会设置它的联机模糊位。

联机模糊位在最后一个实例正常关闭或者立即关闭的时候被清除。其他清除该状态位的场景有:(i)崩溃恢复结束;(ii)当介质恢复进程到了崩溃恢复结束时做检查点(刷新所有脏数据)(见5.5);(iii)当将数据文件临时或正常脱机时(如在检查点之前将文件脱机);(iv)当发出开始热备份指令(见 4.1)。

在8.1节我们将见到如果有数据文件是联机模糊的,那么以重置日志方式打开数据库将会失败。

 


3.6  数据文件检查点事件

数据文件检查点事件发生时,所有实例(所有打开的线程)都将强制刷新指定SCN之前的所有重做记录保护的脏数据。完成后,数据文件头部的检查点将被更新别写到磁盘上。

数据文件检查点事件发生在如开始热备份(见第4节)和将表空间的部分数据文件正常脱机时。

 


3.7  日志切换

当实例需要产生一些重做记录而重做日志中却没有充足的空间时,就会发生日志切换。首先是找到一个联机的重做日志文件候选。

候选日志条件一是该重做日志文件的状态不是启用的。即它不能是崩溃或实例恢复还需要的重做日志文件。换句话说,覆盖它不能造成实例恢复需要的重做记录丢失。强制执行的原则是联机重做日志不能被重用,除非该线程检查点已经超出该重做日志最后一笔重做记录对应的检查点。因为实例恢复时是从当前检查点SCN开始,根据RBA再查找联机重做日志。而日志切换的候选日志是实例恢复不需要的,则说明当前检查点SCN应该超出候选日志的最高SCN。如果不是这样,说明候选日志上正在进行检查点操作(还没有做完),此时不能切换到日志上。

候选日志条件二是它已经完成归档了,当然前提是数据库必须运行在归档模式下。如果没有,则通知归档进程进行归档。归档未完成之前,此时也不能切换到该日志上。

当日志切换完成时,在新的日志上会有个线程检查点操作。期望在检查点操作能在下一次日志切换来临前完成。

 


3.8  归档中的日志切换

在并行服务器环境中,由于每个线程都独立切换日志,各个线程中的日志的起始SCN值并不一样。但是对于启用状态的线程的归档日志的SCN范围要求大致相同。这保证每个线程的最后一笔归档日志都是当前日志。如果一个启用状态的线程的日志未归档,且含有一个比较老的SCN(当该线程的实例活动很少时常发生),它将不能用主库的归档日志来把备库站点恢复到一个更高的SCN。尤其是该日志没有包含重做日志时。

解决这个问题的方法就是当其他线程的当前的日志明显比当前线程的归档日志还要落后时就强制其他线程做日志切换。如果其他线程是打开的,则有一个锁专门促使缓慢的实例去切换日志并归档;如果其他线程是关闭的,则当前活动的线程代替它做日志切换并进行归档。你会注意到这会导致一种现象就是一个线程处于启用状态但却从未使用却有一堆只有文件头的归档日志。该强制归档时的SCN会维护在控制文件中,Oracle会努力去对所有小于或等于该SCN的联机日志进行归档。通常SCN最小的日志会首先被归档。

命令ALTER SYSTEM ARCHIVE LOG CURRENT 用于手动对所有启用状态的线程的当前日志进行归档。它强制所有启用的线程,无论打开还是关闭的,切换新日志,然后归档所有旧的日志,直至发出命令前所有的重做日志都被归档才返回。这个命令保证了热备份恢复时所需要的所有日志都得到归档。它还保证了主库及时往备库传送归档日志以防止灾难。

 


3.9  线程打开(Thread Open)

实例打开数据库的时候,会打开一个线程用于产生重做日志。这个线程在数据库被挂载选定的。实例初始化文件中有一个参数可以指定实例打开的线程号。如果未指定实例就从公共的线程中选择一个。线程上有个锁用于防止两个实例打开同一个线程。实例打开线程的时候会在控制文件中设置线程打开标志位。每个活动的实例都持有一组线程打开锁(分别为LGWR,DBWR,LCK0,LCK1等持有)。实例关闭的时候会释放这些锁,用于实例检测并行服务器环境中其他实例是否活动。见5.1)。同时实例打开线程的时候的检查点将作为线程检查点。如果是数据库首次打开,则该检查点也是数据库检查点,同时推进所有联机数据文件的检查点。注意线程打开的时候也可能会发生一次日志切换。

 


3.10  线程关闭(Thread Close)

实例关闭数据库的时候或者一个线程被实例/崩溃恢复,线程都会关闭。线程关闭的第一步就是保证不在产生重做,然后将该线程产生的日志保护的所有脏数据都刷新到磁盘上。

数据库正常关闭的时候,这个是由线程检查点事件完成。最后一次检查点时的SCN就是线程关闭时的SCN。最后线程在控制文件中的线程打开标志位被清除。

如果线程是被实例恢复结束而关闭,重做程序将从该线程最近一次线程检查点记录开始对数据文件应用该线程对应的重做日志,直至日志的结束。一旦该线程重做日志保护的所有脏数据都刷新到数据文件后,线程检查点推进到线程结束时的检查点。正常的线程检查点可能会推进数据库检查点。如果这是最后一个关闭的线程,数据库在控制文件中记录的检查点线程值将设置为该线程,即使线程已经关闭。

 


3.11  线程启用(Thread Enabled)

要打开一个线程,首先得启用该线程。这保证在介质恢复的时候能够找到该线程的重做日志。线程可以以公共或私有的方式启用。一个私有的线程只能被在初始化文件中系统参数指定的实例打开。这跟回滚段的使用有点类似。一个线程启用后必须至少有两组重做日志组,其中一个重做日志组是当前正在使用的,它的下一个SCN 记录是无限大,以保证新的SCN永远落在当前日志的SCN范DATABASE ENABLE THREAD)。该线程启用记录用于介质恢复应用新的线程的重做记录。也就是说介质恢复打开了另外一个线程。数据库创建的时候会自动启用一个线程,从而避免了‘先有鸡还是先有蛋’的争论。这也是说如果一个数据库不是运行在并行服务器环境下就不需要再启用新的线程。

 


3.12  线程禁用(Thread Disabled)

如果线程长时间不用,就禁用它。这也就是说介质恢复不需要该线程的重做日志记录。当一个线程被禁用的时候,它的重做日志文件可能会被删除。线程禁用之前必须先关闭它。这保证所有脏数据都刷新到磁盘上。新的SCN会作为该重做日志的下一个SCN。日志文件头也会记录该SCN同时表明这个线程已经被禁用了。这个新分配的SCN很重要,它保证了该线程任一个检查点中的SCN在该线程的一个重做日志中。也意味着禁用一个线程的时候必须打开另外一个线程,不能禁用所有线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: