恢复种类
Oracle把恢复分成两类,实例恢复(Instance Recovery)和介质恢复(Media
Recovery)。前一种恢复通常是因为数据库异常关闭,再次启动后由Oracle自动执行的,不需要DBA介入。而后者通常是因为数据文件被破坏,必须要借助备份文件所进行的恢复,必须要有DBA的参与。实例恢复可以细分成实例恢复(Instance
Recovery)和Crash Recovery两种。介质恢复又根据丢失的数据量又分成完全恢复(Complete
Recovery)和不完全恢复(Incomplete Recovery)。
每次数据库打开时,Oracle会进行一系列检查,判断上次的数据库关闭是否正常关闭,是否需要进行实例恢复;以及判断数据文件、控制文件是否来自于某个备份,是否需要进行介质恢复。下面我们就看一下Oracle的检查方法,下面这几个SQL并不是Oracle真正执行的SQL,只是用于说明问题。
首先是对SCN的检查。
SQL>SELECT
a.name,
a.checkpoint_change#,
b.checkpoint_change#,
CASE
WHEN ((a.checkpoint_change# - b.checkpoint_change#) = 0)
THEN 'Startup Normal'
WHEN ((a.checkpoint_change# - b.checkpoint_change#)>0)
THEN 'Media Recovery'
--数据文件头的SCN要比控制文件中的小,也就是说数据文件来自于一个早期的备份
WHEN ((a.checkpoint_change# - b.checkpoint_change#)<0)
THEN 'Old Control File'
--数据文件头的SCN要比控制文件中的大,也就是说控制文件来自于一个较早的备份
ELSE '?'
END STATUS
FROM v$datafile a, -- control file SCN for datafile
v$datafile_header b -- datafile header SCN
WHERE a.file# = b.file#;
虽然控制文件的SCN和数据文件头的SCN相同,也有可能在一次检查点之后立即发生异常关闭,接下来就需要判断是否需要进行Crash
Recovery。
数据库中的每个日志线程都有一个状态,记录于v$thread.status,在数据库正常工作期间,这个状态是OPEN的。如果数据库正常关闭,就会进行一个检查点动作,并把这个状态设成CLOSED;如果数据库是异常关闭的(比如shutdown
abort),数据库就没有机会改变这个状态。因此,在数据库monut阶段,通过检查这个状态,就可以判断数据库是否需要进行Crash
Recovery。这个检查语句是这样的:
SQL>SELECT
a.thread#, b.open_mode, a.status,
CASE
WHEN ((b.open_mode='MOUNTED') AND (a.status='OPEN'))
THEN 'Crash Recovery req.'
WHEN ((b.open_mode='MOUNTED') AND (a.status='CLOSED'))
THEN 'No Crash Rec. req.'
WHEN ((b.open_mode='READ WRITE') AND (a.status='OPEN'))
THEN 'Inst. already open'
ELSE 'huh?'
END STATUS
FROM v$thread a,v$database b,v$instance c
WHERE a.thread# = c.thread#;
图9-9描述了这部分的逻辑。
Instance
Recovery
如果实例被SHUTDOWN
ABORT方法强行关闭,或者因为断电等事故发生故障。数据文件、控制文件、联机日志都没有丢失,这时数据库再次启动时,要利用联机日志的内容进行恢复,这种恢复就是实例恢复(Instance
Recovery)。
Instance
Recovery主要包括3个阶段:
根据联机日志内容进行ROLLOVER;
打开数据库,提供服务;
SMON或用户进程进行ROLLBACK。
图9-9 数据库启动时的判断
Crash Recovery(1)
到了RAC环境下,同样也有Media
Recovery和Instance Recovery,并且RAC还有Crash Recovery。
简单地说,Instance
Recovery是指所有的实例都发生Crash后进行的Recovery,从发生地点来看,这种Recovery发生在故障实例上,是故障实例重新启动时执行的Recovery。
而Crash
Recovery是指某个实例发生了Crash后在其他实例上进行的Recovery。这里最重要的区别是发生地点不是在故障节点,而是在某个健康节点。这种Recovery有一个特殊要求:在健康节点执行Crash
Recovery时,必须要保证故障节点不能再对共享数据进行操作,也就是要对故障节点进行IO隔离(IO
Fencing),这是由CSS服务来保证的。
在单实例环境下,Crash
Recovery和Instance Recovery没有区别。只有在RAC这种多实例环境下,这两个名词的区别才能体现出来。
说明:对于Crash
Recovery最重要的是,必须保证在执行Crash Recovery时,故障节点被IO Fencing。
在Crash Recovery过程中PCM
Lock起到了重要作用,恢复实例(执行Recovery动作的实例)根据数据块的PCM-lock状态来决定数据块是否需要进行恢复。下面回顾一下PCM-Lock在RAC中的使用。
1.PCM-Lock回顾
Oracle无论读还是修改记录,都必须先把记录所在的数据块(Data Block)从磁盘读到内存中(Data Buffer
Cache),这个内存中的拷贝一般叫作Buffer Copy。根据Buffer
Copy和磁盘上的内容是否一致,又分成Dirty和Clean两种状态。
注意Dirty或者Clean和事务提交没有任何关系,只是用来描述Buffer和Disk上内容的一致状态的。当执行Commit操作时,Oracle只会把Log
Buffer中的内容写到联机日志文件中,并不会把修改后的数据块内容同步到磁盘,在这一时刻磁盘和内存中的数据内容不一致,内存中的就叫作Dirty
Block。反过来,如果记录被修改了,但是并没有Commit,而Oracle因为某种原因,比如Free
Buffer不够了,会把修改同步到磁盘,即磁盘和内存中内容一致,这时内存中的就叫Clean Block,这一点需要注意。
而PCM-Lock就是用来描述数据块的Buffer
Copy在不同实例间的分布情况。PCM-Lock有3个属性MODE、ROLE、PAST IMAGE。
(1)MODE属性用来控制不同实例对Buffer Copy的操作能力,有3个值。
Share (S):本实例能够读取这个数据块内容,允许其他实例获得Share lock。
Exclusive (X):本实例能够修改数据块内容,其他实例不能读、修改数据块。
Null (N):本实例不能访问这个数据块内容。更准确地说,这个mode代表这个数据块的buffer
cache可以被清空重用。
(2)ROLE属性描述数据块的分布情况,有2个值。
Local:代表只有当前实例拥有这个数据块的Buffer Copy;如果是X-mode
lock,代表内存和磁盘的内容不一致,是Dirty Buffer;如果是S-mode lock代表内存和磁盘内容一致。
Global:代表有多个实例对这个数据块进行过修改,如果需要清空这个内存块时,必须联系GRD,由最后作修改的那个实例把它写到磁盘。
(3)Past Image这个属性代表实例是否曾经对该数据块做的修改。
2.PCM-Lock的变迁过程
假设一个四节点的RAC,某个数据块最初的磁盘版本SCN = 1000。该数据块的Resource
Master是实例4。下面通过表9-2描述一下PCM-lock的变迁过程。
其中PCM-lock的状态为MRP,比如SL0代表Mode = S、Role = Local、PastImage =
0。
表9-2 PCM变迁过程
动
作
实 例 1
实 例2
实例3
实例4
(Master)
说明
场景1:实例3想要读取数据块
实例3向实例4发送请求;
获准;实例4更新GRD
→SL0
SCN = 1000
场景2:实例2想要读取数据块(并发读)
实例2向实例4发送请求;
实例4通知实例3;
实例3发送数据块;
实例2收到后通知实例4;
实例4更新GRD
→SL0
SCN= 1000
SL0
SCN = 1000
场景3(继续场景1):实例2想要修改这个数据块(读并发写)
实例2向实例4发送X
请求;
实例4依次给每个实例
发送通知,释放其持有的
空间;到最后一个(实例3)
时通知传送数据块;
实例3发送数据块,并释
放自己的内存;
实例4收到后,通知GRD
,并修改数据块SCN = 1100;
实例4更新GRD
→XL0
SCN = 1100
Crash
Recovery(2)
3.PCM-Lock在Crash Recover中的作用
了解了PCM-Lock在Cache
Fusion机制中的变迁过程后,Oracle就可以在Crash
Recovery时根据其他节点上的PCM-lock推断出哪些数据块需要恢复。表9-3总结了这些推论。
拥有X
mode,可以肯定拥有的是数据块的current version。
如果节点拥有了Local role,该节点拥有的是current version。
如果节点是G1的,说明本节点修改之后,其他节点又做过修改。
表9-3 PCM Lock在Crash Recovery中的作用
存活节点PCM Lock
推
论
动
作
L(S/X)0
这种数据块不需要进行恢复
从recovery set中移
除这个数据块
G(S/X)(0,1)
这种数据块不需要恢复
不需要恢复,从
recovery set中把这个
数据块移除;
但会把这个数据块同
步到磁盘,因为
磁盘数据旧
NG(1,2)
这个数据块需要恢复。恢复
的起点是最后一个P
I(根据SCN)
确认哪一个PI作为
recovery buffer;所谓
recovery buffer就是对
data buffer加上
“in-recovery”标志,
表示需要恢复
没有任何锁
这个数据块是被另一个实例使
用过,但是持有的锁状态在
另一个实例上。需要恢复,并
且恢复的起点是磁盘上的内容,
必须先从磁盘读入数据
从磁盘读到recovery buffer
4.Crash Recover过程
Crash
Recover分成3个阶段:First-Pass Log Read、Recovery Claim
Locking和Second-Pass Log Read。从名字可以看出,对故障节点的联机日志要进行两次读取。
(1)First-Pass Log
Read这个阶段要读取故障节点的联机日志,找出所有被修改的数据块,并根据是否有BWR记录,找出可能没有被同步到磁盘上的修改。注意,这个阶段只需要读取联机日志内容,而不需要读取数据文件内容,具体过程如下:
读取故障节点的联机日志内容构造Recovery Set;
每个被修改的数据块会被记录两个SCN,FIRST-DIRTY SCN和LAST-DIRTY
SCN,代表在这个实例上第一次被修改时的SCN和最后一次修改的SCN;
第一次遇到修改某个数据块的Redo Log Record时,其数据块地址被记录,SCN会同时记入FIRST-DIRTY
SCN和LAST-DIRTY SCN;
后续遇到的对该数据块的修改,只更新LAST-DIRTY SCN,而FIRST-DIRTY SCN不变;
如果遇到该数据块的BWR记录,表明之前的修改都已经被同步到磁盘了,因此之前的Redo Log Record被抛弃;
最终,这些数据块会会按照数据块地址(DBA)组成一个Hash Table,这个Hash Table就叫作Recovery
Set。
说明:BWR(Block Write
Record)当某个实例要清空脏数据块所占据的空间时,会通知GRD,再由GRD通知持有该数据块CURRENT版本的实例完成这个写操作,写完成后,实例同时会在联机日志中记录一条BWR记录,表示这个时刻,磁盘版本和SGA中的版本是一致的;同时GRD也会通知所有持有这个数据块PI版本的实例,释放其PI,这些实例除了释放PI也会在日志中记录BWR;这样做的好处,不管故障实例是哪一个实例,都能够知道这个数据块是不需要恢复的。
(2)Recovery Claim Locking这一阶段主要工作是确认Recover Set获得Lock。
在上一阶段只是找出了被故障节点修改过、但是"可能"还没有同步到磁盘的Recovery
Set,注意这里的可能。也就是说,有些数据块可能已经被同步到磁盘上了,这种数据块就不需要恢复。这个阶段Oracle就要根据存活节点上对这些数据块持有的PCM
Lock状态来判断哪些数据库真正需要恢复。具体的判断算法上文已经介绍了。
(3)Second-Pass Log
Read这一阶段会再次读取联机日志,和第一个阶段不同的是,这个阶段还要读取数据文件内容。主要工作如下:
再次读取故障节点的联机日志;
对每条Log
Record检查Recovery Set,看这个Log是否属于需要恢复的数据块;
如果不是则抛弃;如果是则对Recovery Buffer进行恢复;
判断恢复后的SCN是否等于Recovery Set中的LAST-DIRTY SCN,如果是,则恢复完毕;
SMON通知DBWR把这些Recocvery
Buffer同步到磁盘,如果其他实例拥有这个数据块的PI版本,则通知其释放PI;
所有的数据块都恢复完毕后,执行Checkpoint,并释放IR Lock。
无论是Instance Recovery还是Crash
Recovery,都是Oracle自动执行的,DBA无法进行干预,但是可以通过一些参数缩短这个恢复过程,以减少数据库启动时花费的时间