Oracle恢复内部原理

  1 简介

  Oracle数据库提供了下列两类失败模式下的数据库恢复:

  1.实例失败:丢失了Oracle数据缓存中的数据或者内存中的数据

  2.介质失败:丢失了数据库文件

  上面两种模式的任一种失败情景,在恢复的时候想要保证数据库一致性,都有一些前提条件必须满足。

  虽然恢复的过程有一些共同点,但前提条件的差异使得恢复的执行也有很大差异:

  1.实例恢复:恢复Oracle数据缓存中丢失的数据

  2.介质恢复:恢复数据库文件丢失的数据

  1.1 实例恢复和介质恢复的共同的机制

  实例恢复和介质恢复都依赖重做日志。重做日志由一些重做日志线程组成。单实例环境中重做日志只有一个重做线程,多实例环境中每个实例都有一个重做线程。

  一个重做日志线程指的的是一组存放在操作系统上的文件,文件里记录了该实例对数据库的所有变更--已提交的变更和未提交的变更(后者指还存在Oracle数据缓存区中的数据块变更)。因为实例也修改了回滚段中的块,所以回滚段的变更也记录在重做日志线程中。

  实例恢复和介质恢复的第一步都是前滚。前滚属于数据库恢复层面的。在前滚的过程中,重做日志中记录的数据变更被重新应用到数据文件中。因为回滚段的变更也记录在重做日志中,所以前滚过程还会重新构建回滚段块。当前滚结束时,重做日志中记录的所有变更都应用到数据文件上了。此刻,数据块不仅包含了已经提交的数据,也包含了一些未提交的数据。

  实例恢复和介质恢复的第二步就是回滚。回滚属于数据库事务层的任务。回滚过程中,回滚段中记录的由前滚导致的未提交的事务所做的修改将被撤销。

  1.2 实例失败和恢复,崩溃失败和恢复

  实例失败指当实例突然终止时(如因为shutdown abort或主机掉电),实例数据缓存中的内容就都丢失了。

  崩溃失败指数据库的所有实例都同时失败。单实例环境中实例失败等同于崩溃失败。崩溃恢复指的是将所有实例都恢复到崩溃前的一致状态。这一切都是在命令alter database open 之后自动进行的,用户无法干预。

  实例失败会损害数据库的一致性因为它导致该实例的脏数据丢失。所谓“脏数据”就是指实例数据缓存中的数据块内容比数据文件上的要新。当实例崩溃时,还没有来得及将脏数据写入到数据文件中。之所以导致存在这个脏数据丢失问题是因为Oracle的缓存管理采用的是有利于事务处理性能的算法而不是有利于防止实例崩溃的。如下这些有利于性能调优的缓存管理算法使得实例恢复过程有点复杂:

  1.LRU(最近最少使用)缓存替换算法

  2.提交时不强制将脏数据刷新到数据文件中

  上面的算法导致实例失败时对数据库完整性的损害体现在如下几点:

  A.在实例崩溃时,数据文件中可能包含一个原子事务修改的所有块中的部分块而不是全部

  B.在实例崩溃时,数据文件中可能包含一些未提交事务修改的块

  C.在实例崩溃时,一些已提交事务修改的块可能还没有刷新到数据文件中,数据文件中包含的是该事务修改之前的数据块。

  在实例恢复过程中,数据库恢复层修复了上面的损害点A和C,然后后续的数据库事务层修复了损害点B。

  除了那些用来修复数据库完整性损害的前提条件外,实例恢复还需要满足一些前提条件:

  1.实例恢复必须在联机的数据文件上进行恢复。

  2.实例恢复必须使用联机重做日志文件,不能要求使用归档重做日志文件。虽然实例恢复也可以通过使用归档重做日志文件进行恢复(数据库运行在非归档模式除外),但那种恢复过程在要求用户先还原归档日志文件的的时候是不能自动进行的。

  3.实例恢复过程的调用是自动的,隐含的在下次数据库启动的时候被调用。

  4.实例恢复过程中侦测修复的文件或修复过程本身都是自动进行的,无需人工干预。

  5.实例恢复中前滚时间的长短是由Oracle数据库内部机制(checkpoint)和用户配置的参数(如日志文件的大小和数量,checkpoint的频率,并行恢复的参数等)决定的。

  综上所述,Oracle的内存管理策略适合于性能调优而不是降低实例崩溃的影响。本文描述了Oracle为解决采用这种LRU和提交不刷新数据块的算法带来的问题所用到的一些内部机制。这些机制保证了实例恢复的前提条件得到满足同时又兼顾了数据库性能。这些机制如下:

  1.提交前先刷新日志块

  这个机制修复损害C,保证了在事务提交的时候,所有跟该事务有关的重做日志记录包括提交记录都已经写入到重做日志文件中。

  2.检查点机制

  界定了实例恢复时必须应用的重做日志的量。这一点跟联机日志切换结合起来使用确保实例恢复的时候只需要联机重做日志和当前联机数据文件。

  3.联机重做日志切换机制

  跟检查点机制结合起来使用确保实例恢复的时候只需要联机重做日志和当前联机数据文件。它保证当前的检查点总是超前即将被重用的联机重做日志文件。

  4.写日志优先

  这个机制修复损害A,B,因为a)在实例崩溃时刻,数据文件上的所有变更都在重做日志中找到记录;b)所有数据块在写到数据文件之前都先写入跟回滚段和数据块的重做记录。

  5.写重做日志记录是原子的

  这个机制可以修复损害A,B。(注:每笔重做日志记录都是由三个部分组成:重做日志记录头,回滚段改变向量,数据库改变向量。这三部分在写入重做日志时是原子的,不可分割的!)

  6.线程打开标志位

  用于数据库启动时判断是否需要崩溃恢复。

  1.3 介质失败和恢复

  实例失败影响逻辑上的数据库完整性。因为实例失败的时候数据文件是可以恢复到一致状态的,实例恢复以当前数据文件为起点,用联机重做日志进行恢复。

  介质失败则不同,影响的是物理上的数据库完整性或可用性。因为数据文件已经损坏,介质恢复要先还原该数据文件的备份作为介质恢复的起点,用归档重做日志和联机重做日志做前滚操作,直至数据文件备份后实例崩溃前最近的一个一致状态或者数据文件备份后实例崩溃前的任意一个一致状态。介质恢复操作必须由下面命令来执行:RECOVER DATABASE, RECOVER TABLESPACE, RECOVER DATAFILE。

  根据失败场景分析,介质失败对数据库完整性的破坏可能跟实例失败一样。如当一个块被读入到数据缓冲区中修改后正要被DBWR进程将更新后的数据块写回到数据文件中时发生I/O故障,也可能导致前面提到的A,B,C三点损害.此外,介质失败时不仅仅是当前脏数据永久丢失了,而且该数据文件上自上次备份后的所有更新的都丢失了。

  在介质恢复之前,必须先还原被损坏的数据文件。然后在这些数据文件上应用相关的归档日志和联机重做日志前滚到介质失败前的一致状态。

  介质恢复和实例恢复上面提到A,B,C三种损害有一些共同的前提条件。然而介质恢复和实例恢复的前提条件还是有如下五点不同:

  1.介质恢复前必须先还原受损坏的数据文件。

  2.介质恢复除了要求联机重做日志外还要有归档重做日志。

  3.介质恢复必须显示调用,需要人工干预。

  4.介质失败不能自动被侦测到。只有在某个数据文件或数据库备份被还原的时候才能自动侦测到需要介质恢复。

  5.介质恢复所用的前滚时间长短是由用户备份策略决定的(如备份的频率,并行恢复参数等),而不是有Oracle内部机制决定。

  2 基础数据结构

  2.1 Controlfile

  控制文件包含了数据库中所有其他文件的状态信息。

  控制文件包含了如下几类数据:

  A.数据库信息记录(一条)

  B.数据文件记录(每个数据文件一条)

  C.线程记录(每个线程一条。注:每个实例一个线程)

  D.日志文件记录(每个日志文件一条)

  E.文件名记录(每个数据文件或者日志文件成员一条)

  F.日志历史记录(每个已经完成的日志文件一条)

  控制文件的被后面文档引用到的字段如下,后面是引用该字段的章节:

  2.1.1 数据库信息记录(控制文件)

  所含字段:

  A.resetlogs timestamp: 8.2

  B.resetlogs scn: 8.2

  C.enabled thread bitvec: 8.3

  D.force archiving scn: 3.8

  E.database checkpoint thread(thread record index) : 2.13, 3.10

  2.1.3 数据文件记录(控制文件)

  A.thread checkpoint structure: 2.12, 3.4, 8.3

  B.thread-open flag: 3.9, 3.11, 8.3

  C.current log (logfile record index)

  D.head and tail (logfile record indices) of list of logfiles in thread: 2.8

  2.1.4 日志文件记录(控制文件)

  A.log sequence number: 2.7

  B.thread number: 8.4

  C.next and previous (logfile record indices) of list of logfiles in thread: 2.8

  D.count of files in group: 2.8

  E.low SCN: 2.7

  F.next SCN: 2.7

  G.head and tail (filename record indices) of list of filenames in group: 2.8

  H."being cleared" flag: 10.3

  I."archiving not needed" flag: 10.3

  2.1.5 文件名记录(控制文件)

  A.filename

  B.filetype

  C.next and previous (filename record indices) of list of filenames in group: 2.8

  2.1.6 日志文件历史记录(控制文件)

  A.thread number: 2.11

  B.log sequence number: 2.11

  C.low SCN: 2.11

  D.low SCN timestamp: 2.11

  E.next SCN: 2.11

  2.2 数据文件头

  数据文件头部分的被后面文档引用的字段如下,后面跟的是引用该字段的章节:

  A.datafile checkpoint structure: 2.14

  B.backup checkpoint structure: 4.1

  C.checkpoint counter: 2.16, 3.4, 5.3, 6.2

  D.esetlogs timestamp: 8.2

  E.resetlogs SCN: 8.2

  F.creation SCN: 8.1

  G.online-fuzzy bit: 3.5, 6.7.1, 8.1

  H.hotbackup-fuzzy bit: 4.1, 4.4, 6.7.1, 8.1

  I.media-recovery-fuzzy bit: 6.7.1, 8.1

  2.3 日志文件头

  日志文件头部分的被后面文档引用的字段如下,后面跟的是引用该字段的章节:

  A.thread number: 2.7

  B.sequence number: 2.7

  C.low SCN: 2.7

  D.next SCN: 2.7

  E.end-of-thread flag: 6.10

  F.resetlogs timestamp: 8.2

  G.resetlogs SCN: 8.2

  2.4 改变向量(Change Vector)

  改变向量表示对数据块的一次变更。改变向量头部记录了发生变更的数据块的DBA地址,该块的版本号,序列值和操作代码。头部以后的内容跟具体的变更操作有关。数据块版本号和序列值是在创建改变向量时从数据块的头部复制过来的。当块被更新后,版本号值就比原来的值大一点,而序列号则被设为1。此后数据块每变更一次,序列值就增长1.

  2.5 重做记录

  一个重做记录是由一组改变向量组成,代表一个数据库变更。如一个事物的重做记录由三部分组成。首先是事务表(回滚段段首)的改变向量,再次是回滚块的改变向量,最后是数据块的改变向量。一个事物可以产生多个重做记录组成。一个重做记录是数据库恢复的最小单位,一个重做记录由多个改变向量组成的机制允许多个数据块被修改并且这些修改要么都发生要么就都没发生,即使发生突然的失败。这种原子性是由数据库缓冲层的一个基础Job来保证的。Oracle恢复保证重做记录是不可分割的,即使在数据库失败的时候。

  2.6 System Change Number (SCN)

  SCN描述了数据库的一次事物提交版本。一次查询也是查询数据库的某个SCN产生时刻时的内容。SCN会被分配和保存在事务的重做记录的头部。SCN也会保存在控制文件和数据文件中。SCN是由48位长的数字组成。

  2.7 重做日志

  所有数据块的变更发生时都是先构建一个重做记录,然后把这个重做记录保存到重做日志中,最后在数据块中应用该变更。恢复的过程就是在老的数据块上应用重做日志是该数据块变成当前的数据块。这个过程在当前版本数据丢失的情况下是必须的。

  当一个重做日志文件写满了时,就会发生日志切换。每个日志是由一个线程号标识,序列号(一个线程以内再分)和该日志跨越的SCN范围。这些信息保存在日志文件头中。

  重做日志文件中的重做记录是根据SCN排序的。此外,重做记录中包含的具体数据块的改变向量也是按SCN递增顺序发生的,即使是跨多个线程(这种情况发生在多个实例环境中)。只有某些重做记录头部包含有SCN信息,但是所有的记录都是在某个SCN被分配后才发生的。日志文件的头部包含了最小的SCN和下一个SCN。最小的SCN是这个日志文件中第一个重做记录对应的SCN。下一个SCN是当前日志序列递增后的下一个日志文件的的最小SCN。当前在使用的重做日志的下一个SCN都是无限大,因为还没有日志文件的序列号比当前日志的序列要大。

  2.8 重做线程

  每个实例产生的重做日志被称为一个重做日志线程。一个日志线程由联机重做日志和归档重做日志(前提是数据库运行在归档模式下)。联机重做日志是由两个或更多个日志组组成,每个日志组由一个或更多重复的日志成员组成。通常说一个日志组,重做日志,联机日志或简称日志都是指一个日志组的成员集合(可能是一个或者多个)。一个重做日志只包含该日志所属的线程记录的日志。各个线程在写日志时分配日志文件的序列号是彼此独立的,各个线程的日志组之间的日志切换也是独立进行的。

  每个日志文件组在控制文件中都有一笔记录描述它的信息,每笔记录通过日志号码查找。注意日志号跟日志组的号码是一致的,而且是全局唯一的(多实例环境中)。每个重做线程的日志文件组列表记录都在每个重做线程记录的后面(类似于一种Head-Detail结构),各个日志文件组的记录中都分别有一个向前和向后的字段记录上一个日志文件组和下一个日志文件组(通过日志文件组号来记录)。日志文件组的成员信息在日志文件组的记录中描述。

  2.9 Redo Byte Address (RBA)

  RBA指定了重做日志的位置,长度为10字节,由三部分组成:日志序号,块号以及块中的字节序号。

  2.10 检查点结构

  检查点结构定义了数据库重做日志的一个点。检查点结构信息保存数据文件头部和控制文件中每个重做线程的记录中。这些检查点结构用于恢复时告诉数据库从哪个重做日志的哪一笔重做记录开始恢复。

  检查点结构的关键字段是检查点SCN和当前激活的线程标志。

  检查点SCN能有效的定位每个激活中重做日志的任一位置(激活的定义见3.11)。对于每个重做线程,检查点SCN就是发生一次提交时的时间点以及对应重做日志的位置,根据重做日志文件头部的重做记录可以发现第一笔重做记录产生时分配的SCN是检查点SCN或更高一点.

  当前激活的线程标志标记了这个检查点SCN被分配时哪个重做线程处于激活状态。注意每个线程在激活时都有一个状态位被设置,不管它是打开还是关闭状态。每个激活的线程都有一个重做日志,重做日志包含该检查点SCN,这点证明了该重做日志存在(联机或者已经归档)。

  检查点结构还还保存了这个检查点SCN被分配时的时间戳,这个时间戳只是输出一些信息便于用户查找日志。

  此外,检查点结构还保存了检查点SCN被分配时的线程的数量和线程中当前RBA地址。显示的存储线程的RBA地址(相对于存储SCN作为线程的隐式的指针)使得在单实例环境中日志序列和归档日志名称更容易找到。

  检查点在一段时间内可以支持高达1023个重做日志线程,总计150字节长。

  2.11 日志历史

  2.12 线程检查点

  每个激活的线程在控制文件中的记录中都包含了一个检查点结构,我们称之为线程检查点。这个检查点中的SCN字段就是线程检查点SCN,还有线程序号和RBA字段指明了这个线程检查点跟哪个SCN相关。

  线程检查点结构在实例每次对其线程发出检查点操作时被更新(见3.4)。检查点发生时,跟该实例相关的线程会把重做日志里记录的小于该线程检查点SCN的重做记录保护的脏数据写入到联机数据文件中。

  线程检查点事件保证了该线程的重做日志中所有小于该线程检查点SCN的重做记录对应的脏数据都被写入到磁盘(注意到如果该线程关闭了,重做日志里就没有SCN比线程检查点SCN还小的记录。)

  实例恢复的时候要保证对应线程的所有重做日志都应用到数据文件中。因为线程检查点SCN以前的重做日志已经被应用了,所以实例恢复可以保证,在启动重做日志进程时只要从线程检查点SCN开始应用,直至重做日志文件的尾部,所有线程的重做日志都被应用。

  2.13 数据库检查点结构

  数据库检查点结构就是所有打开的线程中线程检查点SCN最小的那个线程检查点。数据库检查点线程的序号--当前线程检查点是数据库检查点的那个线程的序号--被记录在控制文件的数据库信息部分。

  因为每个实例都保证对应线程检查点SCN以前的重做日志保护的脏数据已经写到数据文件中,并且数据库检查点SCN是所有线程中线程检查点SCN最小的,所以可以说所有实例中在数据库检查点SCN以前的重做日志记录所保护的脏数据都被写到数据文件中。换句话说是数据库所有联机数据文件都在数据库检查点时发生了检查点操作。这就是一个实例检查点了对应线程时用数据库检查点更新了所有联机数据文件检查点(见下面)的基本原理(见3.4)。

  2.14 数据文件检查点结构

  数据文件检查点结构指的是每个数据文件的头部包含的检查点结构,其中的SCN字段就是数据文件检查点SCN。

  由前面而知,针对每个数据文件,在其检查点SCN以前的所有线程的重做日志保护的脏数据都已经被写入到数据文件。每个联机的数据文件会把它的检查点SCN记录在控制文件中,但这个值可能跟数据文件头部的检查点SCN不一致。Oracle恢复层设计成能接受这种不一致。当实例失败发生在更新数据文件头部的检查点之后提交控制文件“事务”之前时,二者没有及时同步造成不一致。(注:控制文件“事务”是数据库内部机制,独立于Oracle事务层,指的是保证对控制文件任意大的更新都能够自动被“提交”)。

  数据文件检查点执行时(见3.6),数据文件头部的检查点结构会被更新,并且保证所有线程产生的重做日志记录在该检查点SCN以前对应的脏数据都已经被写入到磁盘上了。

  线程检查点事件(见3.4)保证了所有线程产生的所有SCN在检查点SCN以前的重做日志记录对应的脏数据都被写入到磁盘了。线程检查点事件可能会推进数据库检查点(如在单实例环境中;或该线程的检查点最旧了)如果数据库检查点推进了,新的检查点将更新所有联机数据文件的检查点结构(热备份中的数据文件除外,见第4节)。

  介质恢复时要保证对即将恢复的数据文件把任一个线程产生的重做日志记录都要应用上去。由前面知对即将恢复的数据文件来说,每个线程产生的重做日志在该数据文件检查点SCN以前的重做日志都已经得到应用,介质恢复在重新应用日志时只需要从数据文件检查点SCN开始支持恢复结束点。注:用户指定结束的SCN或时间点等(不完全回复),又或者是所有线程的结束点(完全恢复)。

  因为数据文件检查点保存在数据文件的头部中,所以在数据文件的备份中同样也存在。如果是热备份产生的备份,热备份保证了当数据文件处于热备份状态时,即使在复制要备份的数据文件过程中数据库要更新该数据文件上的脏数据,数据文件中所有数据库的版本跟数据文件头部的检查点结构的版本是一致的,都是在热备份开始那一刻时的版本。

  2.15 结束SCN

  每个数据文件在控制文件的记录中都有一个字段叫结束SCN。如果该数据文件是脱机状态或只读的,该结束SCN的值表示对该数据文件不会有再比这个结束SCN更大的重做记录。如果该数据文件是联机的并且有一个实例打开了数据库,结束SCN就会被设置为无限大(注:值为0xffff.ffffffff)。结束SCN用于介质恢复时告诉重做程序在该数据文件上应用重做日志时到了这个SCN时就停止。这保证了在数据库打开的状态下恢复一个脱机的数据文件时介质恢复能停下来。

  不管数据文件脱机或者只读,结束SCN都会被设置为具体的SCN。不管脱机操作时“立即”(因为I/O错误导致或者因为数据文件所在表空间被立即脱机)还是“临时的“或“正常的”(数据文件所在表空间正常脱机)。不过在数据文件被立即脱机时,不会发生数据文件检查点(见3.6),并且对应的脏数据都丢失了。因此在将该脱机文件变成联机时介质恢复需要重新应用重做日志直至结束SCN。介质恢复不需要应用结束SCN以后的重做日志,因为就不存在这样的重做日志。如果结束SCN等于数据文件检查点SCN,那么该数据文件就不需要恢复。

  2.16 检查点计数

  在数据文件头部和数据文件在控制文件的记录部分都有一个检查点技术。检查点计数是用来判断数据文件或者控制文件是否是过时的(从一个备份中恢复出来的)。

  每次发生线程检查点事件时,检查点计数都会递增。从而即使数据文件的检查点没有被推进时检查点计数也会增加。线程检查点事件时数据文件检查点没有被推进的原因可能是数据文件处于热备份状态,或者它的SCN比要更新的检查点SCN值还要新(如数据文件时新增的或者刚经历了一次数据文件检查点事件)。

  2.17 表空间干净结束SCN

  数据字典TS$有两列表示 表空间干净结束SCN(注:9i中是SCNWRP和SCNBAS组合表示)。它表示表空间是在这个SCN时被脱机或者设置为只读。如在对数据文件发出检查点操作后(见3.6),数据文件检查点SCN被记录到TS$中作为表空间干净结束SCN。这样的表空间在数据库以重置日志方式打开后不需要被删除(见8.6)。在介质恢复时,以重置日志方式打开日之前,这种表空间会处于脱机状态。重置日志后,表空间也不需要恢复,允许被直接设置为联机状态或读写状态。重置日志后建议立即备份表空间。

  当把一个脱机且干净的表空间置为联机或者可读写时,表空间干净结束SCN会被设置为0(中间可能短暂性被设置为无穷大)。立即或临时性的将表空间置为脱机状态,tablespace-clen-stop SCN也会是0.

  一个在TS$中表空间干净结束SCN为非零的表空间被认为在那个SCN时是干净的,指的是该表空间包含了直至那个SCN的所有的重做记录保护的数据,没有大于那个SCN的重做日志记录存在。如果该表空间的所有数据文件都是脱机时刻的状态或者只读的,那在将表空间置为联机状态时是不需要恢复的。注意到表空间干净结束SCN同数据文件在控制文件中的记录的结束SCN是有区别的。结束SCN只是表明该数据文件没有超过结束SCN的重做日志记录,但并没有暗示该数据文件已经包含了结束SCN以前的所有重做日志记录所保护的脏数据。

  表空间干净结束SCN存放在TS$中而不是控制文件中,所以可以得到重做日志的保护并且处于正确的状态。如在一个不完全恢复后(见6.12),该表空间的状态依然正确。它的值也不会随着使用了备份的控制文件。更重要的是这种SCN的存在使得该表空间在以重置日志方式打开数据库后依然可以存在。因此不完全恢复期间的脱机了的表空间可以在以重置日志方式打开数据库后直接置为联机状态或设置为可读写。如果没有表空间干净结束SCN,就没有办法知道该表空间不需要被重置日志抛弃的重做日志记录。那时唯一可选的办法就是将该表空间脱机。

  2.18 数据文件脱机范围

  数据文件在控制文件中记录了脱机时的SCN和脱机结束时的检查点。二者界定了该数据文件不需要重做日志的范围。因此介质恢复的时候可以直接跳过该范围的重做日志。这个特性帮助恢复了一个已经脱机了或者设为只读了很长一段时间的数据文件变成联机或可读写。

  当一个数据文件从脱机状态变为联机状态时(或从只读变成可读写时),脱机范围设置规则如下:脱机起始SCN取的是该表空间的表空间干净结束SCN,脱机结束检查点取的是数据文件设置为联机(或可读写)时的数据文件检查点。

  3 重做日志

  重做日志描述了对数据块的所有变更。这一节主要描述数据库打开的时候写日志时的一些操作。

  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 线程打开

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

  3.10 线程关闭

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

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

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

  3.11 线程启用

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

  3.12 线程禁用

  如果线程长时间不用,就禁用它。这也就是说介质恢复不需要该线程的重做日志记录。当一个线程被禁用的时候,它的重做日志文件可能会被删除。线程禁用之前必须先关闭它。这保证所有脏数据都刷新到磁盘上。新的SCN会作为该重做日志的下一个SCN。日志文件头也会记录该SCN同时表明这个线程已经被禁用了。这个新分配的SCN很重要,它保证了该线程任一个检查点中的SCN在该线程的一个重做日志中。也意味着禁用一个线程的时候必须打开另外一个线程,不能禁用所有线程。

  4 热备份

  热备份指在数据文件正在使用的时候对其进行复制。复制的过程中,DBWR也在进行,因此备份可能得到一些“不一致”的备份:

  a.一些数据块可能比其他块的时间要早

  b.一些数据块的SCN可能比数据文件头部的SCN还要早

  c.一些块可能包含了一个重做记录的部分更新,其他的部分可能在这个数据文件上或者其他数据文件上

  d.一些数据块可能会被损坏,因为块的头部和尾部是在不同的时间复制的。

  上面的这种复制方式得到的备份集在还原后和介质恢复的时候是没有用的。介质恢复时会从数据文件上的开始热备SCN(见4.1中第二步)直至恢复过程结束(完整或不完整的恢复)。这样数据文件从事务上讲是一致的。

  热备份一共有三个步骤:

  a.执行命令:ALTER TABLESPACE ... BEGIN BACKUP.

  b.调用操作系统的复制工具复制该表空间下所有数据文件。

  c.执行命令:ALTER TABLESPACE ... END BACKUP

  4.1 BEGIN BACKUP

  BEGIN BACKUP命令实际对表空间的所有数据文件进行 下列操作(不需要按顺序):

  1.在每个数据文件头部设置了热备份模糊标志位,表明该数据文件处于热备份状态。带有这种标记的数据文件头表明这个备份是热备份。标志的目的是冻结这个数据文件头部的检查点(停留在begin backup命令发出那一刻的SCN)。这个值作用在于当备份被还原时,介质恢复能从足够早的SCN开始重新应用重做日志。因为我们不能保证数据文件头首先被复制,因此热备份期间要将数据文件头的检查点冻结支持热备份结束。这个标准也冻结了数据文件的检查点(以及在控制文件中的记录的检查点),以免被线程检查点更新。7.2版本后数据文件头新增了一个备份检查点以接收原本属于被冻结的检查点该接收的更新。

  2.做了一个数据文件检查点操作,捕获了开始热备时候的检查点信息,包括开始热备的SCN。当数据文件被检查点时,所有实例都要刷新跟该数据文件有关的脏数据。如果此时需要实例恢复,检查点将等待恢复完成再继续。在开始备份的时候对数据文件进行检查点操作保证了在热备份期间,只有在发出热备份命令之后的时间里修改的块可能会被写到数据文件上。

  3.[跟平台有关,可选]:热备份后开始数据块的前镜像记录。记录过程中,所有实例将每个要修改块的完整的块而不是修改向量记录到重做日志中。这是为了防止恢复中遇到零碎的块(不一致的块),这多发生在数据块的大小比操作系统的块要大的时候,更新数据块导致前一部分和后一部分分别在不同的时间被复制。在热备份后恢复进程可以利用重做日志中的记录的完整前镜像构建数据块。

  4.设置数据文件头部的检查点跟开始热备份时刻的检查点一致,并冻结它直至发出“END BACKUP”命令。

  5.清除了数据文件的联机模糊位标志。在热备份期间该状态位一直是被清除的,直至发出“END BACKUP”命令。

  4.2 复制文件

  复制是由操作系统的工具来完成。管理员必须保证复制操作发生在“BENGIN BACKUP”和“END BACKUP”之间或者文件没有使用的时候。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值