Q
题目
在Oracle中,实例恢复和介质恢复的区别是什么?
A
答案
Redo日志是Oracle为确保已经提交的事务不会丢失而建立的一种机制。实际上,Redo日志的存在是为两种场景准备的,一种称之为实例恢复(Instance Recovery),一种称之为介质恢复(Media Recovery)。
介质恢复是基于物理备份恢复数据,它是Oracle数据库出现介质故障时恢复的重要保障。介质恢复包括块恢复、数据文件恢复、表空间恢复和整个数据库的恢复。介质恢复主要是针对错误类型中的介质失败,如果是少量的块失败,那么可以使用介质恢复中的块恢复来快速修复;但如果是其它情况的丢失,那么需要根据具体情况,可使用数据文件恢复、表空间恢复甚至全库恢复,可以参考如下的表格:
错误分类 | 恢复解决方案 |
---|---|
介质失败 | 如果是少量的块损坏,使用块介质恢复;如果是大量的块、数据文件、表空间的损坏,可能需要对损坏的数据文件或者表空间执行完全恢复;如果是归档Redo日志文件或者联机Redo日志文件的丢失,那么只需要不完全恢复方式。 |
逻辑损坏 | 如果是程序员错误导致出现的问题,可通过补丁应用修复问题。对于无法修复的问题,也可采用介质恢复手段来恢复数据。 |
用户错误 | 根据不同用户错误,选择不同的FLASHBACK技术恢复,使用FLASHBACK技术恢复用户错误是首选方案。如果FLASHBACK不能很好的恢复数据再考虑使用介质恢复或者表空间时间点恢复。 |
Oracle数据库的介质恢复实际上包含了两个过程:数据库还原(RESTORE)与数据库恢复(RECOVER),如下所示:
l 数据库还原(RESTORE)是指利用备份的数据库文件来替换已经损坏的数据库文件或者将其恢复到一个新的位置。RMAN在进行还原操作时,会利用恢复目录(有建立恢复目录的话就使用目标数据库的控制文件)来获取备份信息,并从中选择最合适的备份进行修复操作。当选择备份时,有以下两个原则:(1)选择距离恢复目录时刻最近的备份;(2)优先选择镜像复制,其次才是备份集。
l 数据库恢复(RECOVER)是指数据文件的介质恢复,即为修复后的数据文件应用联机或归档日志,从而将修复的数据库文件更新到当前时刻或指定时刻下的状态。在执行恢复数据库时,需要使用RECOVER命令。
还原是将某个时间点的数据文件的副本再拷贝回去,还原后的数据库处于不一致的状态,或不是最新的状态,还需要执行恢复操作。恢复就是使用归档日志文件和联机Redo日志文件将不一致的数据库应用到一致性状态。需要注意的是,还原只是建立在数据库备份的基础版本上,例如,如果数据库备份包括0级备份和很多1级备份,还原只是应用0级备份,恢复过程会根据情况自动应用1级备份或Redo日志将数据库恢复到一致性的状态。
数据库的恢复过程根据恢复数据的程度又分为完全恢复(Complete Recovery)和不完全恢复(Incomplete Recovery),如下所示:
l 完全恢复是一种没有数据丢失的恢复方式,能够恢复到最新的联机Redo日志中已提交的数据。在传统恢复方式中,因介质失败破坏了数据文件之后,可以在数据库、表空间和数据文件上执行完全介质恢复。
l 不完全恢复是一种与完全恢复相反的恢复方式,是一种丢失数据的恢复方式,也称为数据库基于时间点恢复(Point-in-Time Recovery),是将整个数据库恢复到之前的某个时间点、日志序列号或者SCN号。通常情况下,若FLASHBACK DATABASE没有启用或者变得无效,则可以执行不完全恢复撤销一个用户错误。不完全恢复不一定在原有的数据库环境执行,可以在测试环境下执行不完全恢复,将找回的数据再重新导入生产库中。不完全恢复根据备份情况恢复到与指定时间、日志序列号和SCN具有一致性的数据,之后的数据都将丢失。执行不完全恢复一方面可能是因为归档日志、联机日志的丢失,另一方面可能是因为在某个时刻错误地操作了数据,过了一段时间之后才发现问题,而其它的恢复手段都无法恢复数据,这时也不得不使用不完全恢复来找回数据。执行不完全恢复必须从备份中还原所有的数据文件,备份文件必须是要恢复的时间点之前创建的。当恢复完成后,使用RESTLOGS选项打开数据库,将重新初始化联机Redo日志,创建一个新的日志序列号流,日志序列号从1开始,RESETLOGS之后的SCN还是在递增。
如果是完全恢复,那么数据库就是最新的一致性状态;如果是不完全恢复,那么数据库就是非最新的一致性状态。对于非归档模式的数据库来说,不能执行不完全恢复。不完全恢复意味着会缺失一些事务处理;即恢复目标时间和当前时间之间所做的所有数据修改都会丢失。在很多情况下,这正是想要的结果,因为可能需要撤消对数据库进行的一些更改。恢复到过去的某一时间点是删除误更改的一种方法。
不完全恢复的选项如下表所示:
不完全恢复方式 | RMAN选项 | 用户管理备份选项 |
---|---|---|
恢复到某个时间点 | UNTIL TIME | UNTIL TIME |
恢复到某个日志序列号 | UNTIL SUQUENCE | UNTIL CANCEL |
恢复到某个SCN号 | UNTIL SCN | UNTIL CHANGE |
不完全恢复的几种类型如下表所示:
综上所述,恢复的分类如下图所示:
实例恢复可确保数据库在一个实例失败后仍能回到一个一致性的状态。Redo日志记录了对实例的所有更改。单实例数据库拥有一个重做线程,而一个RAC数据库拥有多个重做线程,且RAC数据库的每个实例拥有一个重做线程。当事务提交时,LGWR将内存中的重做条目和事务SCN同时写入联机Redo日志。但是,DBWn进程只在最有利的时机将已修改的数据块写入数据文件。所以,未提交的更改可能会暂时存在于数据文件中,而已提交的更改也可能还不在数据文件中。
当数据库突然崩溃,而还没有来得及将Buffer Cache里的脏块刷新到数据文件里,同时在实例崩溃时正在运行着的事务被突然中断,则事务为中间状态,也就是既没有提交也没有回滚。这时数据文件里的内容不能体现实例崩溃时的状态。这样关闭的数据库是不一致的。当下次启动实例时,Oracle会由SMON进程自动进行实例恢复。实例启动时,SMON进程会去检查控制文件中所记录的、每个在线的、可读写的数据文件的End SCN号。在数据库正常运行过程中,该End SCN号始终为NULL,而当数据库正常关闭时,会进行完全检查点,并用检查点SCN号更新该字段,所以可以通过End SCN号是否为NULL来判断是不是需要实例恢复。在数据库实例崩溃时,Oracle还来不及更新该字段,则该字段仍然为NULL。当数据库再次启动时,SMON进程发现该字段为空时,就知道实例在上次没有正常关闭,于是由SMON进程就开始进行实例恢复了。
对于单实例的数据库而言,实例恢复一般是在数据库实例异常故障后、数据库重启时进行,当数据库执行了SHUTDOWN ABORT或者由于操作系统、主机等原因宕机重启后,在执行ALTER DATABASE OPEN的时候,就会自动做实例恢复。在RAC环境中,如果某个实例宕机了,那么剩下的实例将会代替宕掉的实例做实例恢复。除非是所有的实例都宕机了,这样的话,第一个执行ALTER DATABASE OPEN的实例将会做实例恢复。这也是在RAC环境中,Redo日志是实例私有的组件,但是Redo日志的文件必须存放在共享存储上的原因。
实例恢复使用检查点来确定必须将哪些更改应用到数据文件。检查点位置始终保证所有比其SCN低的检查点所对应的已提交更改都已保存到数据文件。
在实例恢复期间,数据库必须应用检查点位置和重做线程结尾之间发生的更改。如上图所示,某些更改可能已经写入数据文件。但是,只有其SCN低于检查点位置的更改,才保证已被写到了磁盘上。
在实例发生异常终止的情况下,数据库处于以下的状态:
① 事务提交的数据块只写入联机Redo日志中,没有更新到数据文件(那么未写入数据文件的更新必须重新写入数据文件)。
② 由于DBWn进程是异步向磁盘写入数据的,所以,数据文件中可能包含没有被提交但已经写入数据文件的改变,这些改变必须回滚到之前的状态,以确保数据的一致性。
实例恢复利用联机Redo日志文件解决第一个问题,利用Undo数据同步数据文件解决第二个问题,从而确保数据库数据的一致性。因此,实例恢复过程会经历两个阶段:前滚(Rolling Forward)和回滚(Rolling Back),如下图所示:
① 实例恢复的第一阶段称为前滚(Rolling Forward)或者缓存恢复(Cache Recovery)。前滚会将数据文件还原到实例出现错误之前所处的状态。SMON进程在进行实例恢复时,会从控制文件中获得检查点位置(Checkpoint Position),然后SMON进程到联机Redo日志文件中找到该检查点位置,再从该检查点位置开始往下应用所有的Redo日志条目,从而在Buffer Cache里又恢复了实例崩溃那个时间点的状态。这个过程叫做前滚。因为回滚数据记录在联机Redo日志中,所以,前滚也会重新生成相应的Undo段。前滚完成之后就可以确保联机Redo日志中所有已提交的事务操作的数据写回到数据文件中。但是,这些数据文件可能还包含未提交的更改,要么是在实例失败前保存到数据文件中的,或者是在前滚过程中引入的。如果正在执行的检查点还未完全执行完毕时发生实例失败,前滚过程可能需要通过多个联机Redo日志文件才能使数据恢复到之前时间的状态。
② 实例恢复的第二阶段称为回滚(Rolling Back)或者事务恢复(Transaction Recovery)。前滚之后,任何未提交的更改必须被撤消。Oracle数据库使用检查点位置,保证每个低于其SCN的已提交更改都已保存到磁盘。Oracle数据库应用Undo块,以回滚数据块中在实例失败前写入的或前滚过程中引入的未提交更改。这一阶段称为回滚或事务恢复。在前滚完毕以后,Buffer Cache里既有崩溃时已经提交还没有写入数据文件的脏块,还有事务被突然终止,而导致的既没有提交又没有回滚的事务的脏块。前滚一旦完毕,SMON进程立即打开数据库。但是,这时的数据库中还含有那些中间状态的、既没有提交又没有回滚的脏块,这种脏块是不能存在于数据库中的,因为它们并没有被提交,必须被回滚。在打开数据库以后,SMON进程会在后台进行回滚。有时,新事务可以自己回滚个别块以获取所需的数据,而不必等待SMON进程来回滚这些已终止的事务。在数据库打开以后,SMON进程还没来得及回滚这些中间状态的数据块时,就有用户进程发出读取这些数据块的请求。这时,服务器进程在将这些块返回给用户之前,由服务器进程负责进行回滚,回滚完毕后,将数据块的内容返回给用户。Oracle数据库应用Undo块回滚在数据块中未提交的改变,这些数据块是在实例失败之前或者前滚期间被写入的。回滚会将已执行但尚未提交的更改会返回到初始状态。回滚完成之后,整个实例恢复才算完成,而Redo和Undo的丢失或者损坏都可能导致实例恢复失败。Oracle数据库可以根据需要同时回滚多个事务。
总结一下,前滚和回滚是Oracle数据库实例发生意外崩溃,重新启动的时候,由SMON进行的自动恢复的过程。所谓的前滚,是应用Redo来恢复Buffer Cache的数据,将Buffer Cache恢复到Crash之前状态,所以此时Buffer Cache中既有崩溃时已经提交但还没有写入数据文件的脏块,还有事务被突然终止而导致的既没有提交又没有回滚的事务的脏块(也就是没有COMMIT,但是DBWn已经将改变的数据刷新到底层磁盘)。前滚完成之后就可以确保联机Redo日志中所有已提交的事务操作的数据写回到数据文件中。接下来,前滚之后,任何未提交的更改必须被撤消,而回滚是在数据库做完前滚操作后并打开数据库的情况下完成的,SMON会利用Undo信息将未提交的事务全部进行回滚。具体来说,SMON进程在完成前滚后,查看Undo段头(Undo段的第1个数据块)记录的事务表(每个事务在使用Undo块时,首先要在该Undo块所在的Undo段头记录一个条目,该条目里记录了该事务相关的信息,其中包括是否提交等),将其中既没有提交也没有回滚,而是在实例崩溃时被异常终止的事务全部回滚。
那么,为什么数据库的实例恢复是先前滚再回滚呢?回滚段实际上也是以回滚表空间的形式存在的,既然是表空间,那么肯定就有对应的数据文件,同时在Buffer Cache中就会存在映像块,这一点和其它表空间的数据文件相同。当发生DML操作时,既要生成Redo(针对DML操作本身的Redo Entry)也要生成Undo(用于回滚该DML操作,记录在Undo表空间中),但是既然Undo信息是使用回滚表空间来存放的,那么该DML操作对应的Undo信息(在Buffer Cache生成对应中的Undo Block)就会首先生成其对应的Redo信息(Undo Block's Redo Entry)并写入Log Buffer中。这样做的原因是因为Buffer Cache中的有关Undo表空间的块也可能因为数据库故障而丢失,为了保障在下一次启动时能够顺利进行回滚,首先就必须使用Redo日志来恢复Undo段(实际上是先回复Buffer Cache中的脏数据块,然后由Checkpoint写入Undo段中),在数据库OPEN以后再使用Undo信息来进行回滚,达到一致性的目的,生成完Undo Block's Redo Entry后才轮到该DML语句对应的Redo Entry,最后再修改Buffer Cache中的BLOCK,该BLOCK同时变为脏数据块。实际上,简单点说Redo的作用就是记录所有的数据库更改,包括Undo表空间在内。
有关实例恢复和介质恢复的区别如下表所示:
& 说明:
有关实例恢复和介质恢复的更多内容可以参考我的BLOG:http://blog.itpub.net/26736162/viewspace-2126293/、http://blog.itpub.net/26736162/viewspace-2140915/、http://blog.itpub.net/26736162/viewspace-2126407/