oracle实例恢复时,哪些redo是需要重做的?
说一下oracle实例恢复时,要用到哪些redo。至于何时需要实例恢复就不多说了。
我们都知道实例恢复时,需要找到redo的起点(lrba,也就是checkpoint rba:检查点队列中第一脏块的lrba)和终点(on-disk rba)。终点自然就是redo的结尾,起点就比较麻烦了。检查点队列头部的那个脏块第一次被修改对应的日志位置就是lrba,在这个lrba之前的redo所保护的data buffer都已经被写入到磁盘。由于lrba记录的是脏块第一次被修改对应的日志位置,这就是说在检查点队列中当这个buffer被写入磁盘时,这个buffer可能被可能被修改多次,对应着多条redo条目。增量检查点更新控制文件中的lrba后,而这些redo可能会存在lrba之后,然而这些redo所保护的buffer已经写入到磁盘,必然是不需要再重做的。说起来比较绕口,下面看个图吧。
更新table表中的记录,记录行的数据块分布及产生的日志信息如下:
Update table set name=low(name) where id=1; ------块1 RBA :692.2998.10 Update table set name=low(name) where id=2; ------块1 RBA :692.3015.10 Update table set name=low(name) where id=4; ------块2 RBA :692.3024.10 Update table set name=low(name) where id=7; ------块3 RBA :692.3033.10 Update table set name=low(name) where id=3; ------块1 RBA :692.3102.10 Update table set name=low(name) where id=10; ------块4 RBA :692.3127.10 Update table set name=low(name) where id=13; ------块5 RBA :692.3136.10 |
data buffer中checkpoint queue大概就是下面这样:
1号块 | 2号块 | 3号块 | 4号块 | 5号块 |
lLRBA:692.2998.10 HRBA:692.3102.10 |
LRBA:692.3024.10 HRBA:692.3024.10 |
LRBA:692.3033.10 HRBA:692.3033.10 |
LRBA:692.3127.10 HRBA:692.3127.10 |
LRBA:692.3136.10 HRBA:692.3136.10 |
相应的重做记录有:
RBA:692.2998.10(第一条更新命令对应的重做记录)
(下面的重做记录中省略具体的信息) |
|||
RBA:692.3015.10 (第二条更新命令对应的重做记录) |
|||
RBA:692.3024.10 (第三条更新命令对应的重做记录) |
|||
RBA:692.3033.10 (第四条更新命令对应的重做记录) |
|||
RBA:692.3102.10 (第五条更新命令对应的重做记录) |
|||
RBA:692.3127.10 (第六条更新命令对应的重做记录) |
|||
RBA:692.3136.10 (第七条更新命令对应的重做记录) |
现在开始发生增量检查点,dbwr进程开始从检查点队列写数据,假设现在1号、2号被写入,3号块的lrba被更新到控制文件中,一次增量检查完成。数据库突然断电需要实例恢复,从控制文件中找到lrba,开始重做redo到结束。那么显然第5行日志是不需要再做的,因为这条日志所保护的数据已经在上一次增量检查点发生时被写入到磁盘。那么oracle会怎么判断这条日志要不要做呢?
我们知道redo中会记录每个redo record的scn和rba。scn指的是redo产生时当前数据库的时间,如下图:
REDO RECORD - Thread:1 RBA: 0x000040.00000024.01b4 LEN: 0x00e4 VLD: 0x01 SCN: 0x0000.0075933b SUBSCN: 7 02/08/2018 14:10:00 CHANGE #1 TYP:0 CLS:26 AFN:3 DBA:0x00c00b4e OBJ:4294967295 SCN:0x0000.0075933b SEQ:6 OP:5.1 ENC:0 RBL:0 ktudb redo: siz: 88 spc: 3666 flg: 0x0022 seq: 0x066c rec: 0x25 xid: 0x0005.020.000012bb ktubu redo: slt: 32 rci: 36 opc: 10.22 objn: 6035 objd: 6040 tsn: 0 Undo type: Regular undo Undo type: Last buffer split: No Tablespace Undo: No 0x00000000 index undo for leaf key operations KTB Redo op: 0x02 ver: 0x01 compat bit: 4 (post-11) padding: 1 op: C uba: 0x00c00b4e.066c.24 Dump kdilk : itl=2, kdxlkflg=0x1 sdc=0 indexid=0x403100 block=0x00403101 (kdxlpu): purge leaf row key :(15): 07 78 76 02 08 08 0b 01 06 00 41 24 fc 00 08 CHANGE #2 TYP:0 CLS:1 AFN:1 DBA:0x00403101 OBJ:6040 SCN:0x0000.0075933b SEQ:2 OP:10.2 ENC:0 RBL:0 index redo (kdxlin): insert leaf row KTB Redo op: 0x02 ver: 0x01 compat bit: 4 (post-11) padding: 1 op: C uba: 0x00c00b4e.066c.25 REDO: SINGLE / -- / -- itl: 2, sno: 29, row size 19 insert key: (15): 07 78 76 02 08 08 0b 01 06 00 41 24 fc 00 08 |
把数据块dump下来,数据块上记录着上一次增量检查点时,哪些redo保护的数据被写入到磁盘。即lscn与hscn之间的redo。
Start dump data blocks tsn: 0 file#:1 minblk 77057 maxblk 77057 Block dump from cache: Dump of buffer cache at level 4 for tsn=0 rdba=4271361 BH (0xecff7048) file#: 1 rdba: 0x00412d01 (1/77057) class: 1 ba: 0xecf16000 set: 11 pool: 3 bsz: 8192 bsi: 0 sflg: 2 pwc: 659,28 dbwrid: 0 obj: 96602 objn: 96602 tsn: 0 afn: 1 hint: f hash: [0x136ae98a8,0x136ae98a8] lru: [0xecff7270,0xedfbf788] ckptq: [NULL] fileq: [NULL] objq: [0x121ef05a8,0x121ef05a8] objaq: [0x121ef0598,0x121ef0598] st: XCURRENT md: NULL fpin: 'ktswh23: ktsfbkl' tch: 2 flags: block_written_once redo_since_read LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1] Block dump from disk: buffer tsn: 0 rdba: 0x00412d01 (1/77057) scn: 0x0000.0076b969 seq: 0x01 flg: 0x06 tail: 0xb9690601 |
那么我们上面的问题就迎刃而解了。实例恢复的步骤:
①读取控制文件获取lrba,确定恢复所需的redo范围;
②redo重做,判断该行日志的scn与数据块hscn的大小,只有在hscn之后的redo才会被重做,之前的redo保护的数据已经写入磁盘,不需要再做。
上面数据块的dump文件是我在手动checkpoint之后的,发现lscn为0,hscn为无穷大。也刚好证明了上面所说,checkpoint之后所有的脏数据写入磁盘,检查点队列为空。即使实例恢复,对于该块来说也没有redo需要重做。因为在hscn之后不存在该块的redo。如果你的lscn和hscn信息如上面所示,证明这个块的所有脏数据已经被写入磁盘。
- 点赞
- 收藏
- 关注作者
评论(0)