本帖最后由 ubotutwin 于 2012-2-1 10:06 编辑
oracle数据库特种恢复技术(五)--redo篇
作者:谢浩
继续上一篇的内容。
本篇围绕redo日志挖掘展开,可以说是本系列中对数据库恢复最无足轻重的一篇。之所以这样说是因为,只有在数据库及其备份出现严重故障的特殊情况下才会用到本系列所描述的技术,而这种情况下,少量的数据完整性缺失已经是无足轻重的问题了。但本篇所分析的内容对数据变更轨迹的挖掘、数据库同步技术等都有很大意义。
1、对测试表分别进行insert和update操作,并分别记录scn:
SELECTDbms_Flashback.get_system_change_number() from dual;
insert into xh_tempvalues('s11','s21','s31',1,2,'s4',sysdate,sysdate,sysdate,'s5','s','d','f');
commit;
SELECTDbms_Flashback.get_system_change_number() from dual;
SELECT Dbms_Flashback.get_system_change_number()from dual;
update xh_temp a set a.object_name='test1'where a.object_name = 's2';
commit;
SELECTDbms_Flashback.get_system_change_number() from dual;
2、以scn base方式分别dump两个操作的在线redolog:
Alter system dump logfile '/u01/app/oracle/oradata/PHONEDB/onlinelog/o1_mf_3_6lf2hxow_.log'scn min 21184829 scn max 21184832;
alter system dump logfile'/u01/app/oracle/oradata/PHONEDB/onlinelog/o1_mf_3_6lf2hxow_.log' scn min21184178 scn max 21184181;
3、从dump的trace文件中分别获得两个操作的redo记录的rba,使用dd工具复制redolog文件相应位置的二进制代码:
ddif=/u01/app/oracle/oradata/PHONEDB/onlinelog/o1_mf_3_6lf2hxow_.logof=./update.dd count=10 skip=9269 bs=512
ddif=/u01/app/oracle/oradata/PHONEDB/onlinelog/o1_mf_3_6lf2hxow_.logof=./updateC2.dd count=10 skip=9971 bs=512
这里的bs选择512是根据oracle软件redo的block尺寸,redo文件的block size不同于数据文件,而是与os单次io的块大小像匹配的,在linux、aix、windows中默认一次io是512字节,hp ux中默认是1024字节。Linux系统可以通过fdisk命令查看os blocksize。这个block size会在redolog的第一个块的偏移20位置用4字节存储(来自Dissecting the Redo Logs)Oracle这样设计的目的是为了尽量保证redo写入的安全性,保证io异常时不会有“切块”的情况发生,因为真正保证数据库数据的并不是数据文件,而是重做日志。(但是如果物理存储设备带有缓存,还是不能避免会有部分数据丢失) dd输出:
00000000h: 01 22 00 00 F8 44 01 00 FB 02 0000 10 80 38 9F ; .
00000010h: 90 03 00 00 0D 05 00 00 35 60 4201 02 00 06 C5 ; ?......5`B....?
00000020h: 00 00 00 00 61 02 C1 02 00 00 0100 02 00 00 00 ; ....a.?........
00000030h: 02 00 00 00 00 63 61 74 34 60 4201 00 00 75 63 ; .....cat4`B...uc
00000040h: 72 70 01 80 02 C1 20 02 C1 1A 2C00 08 03 C2 4C ; r
00000050h: 46 9D D2 2D 0B 02 01 00 03 00 0000 E6 66 C0 00 ; F澮-........鎓?
00000060h: 0F 60 42 01 00 00 01 00 02 02 73C4 16 00 18 00 ; .`B.......s?...
00000070h: 31 00 13 00 07 00 02 00 00 00 0000 01 00 0D 00 ; 1...............
00000080h: 01 00 AB 0C 01 01 00 00 00 00 0000 05 00 28 00 ; ..?..........(.
00000090h: 29 33 00 00 93 0D 80 00 77 13 3100 E6 66 C0 00 ; )3.
000000a0h: E3 66 C0 00 FA 12 02 01 01 00 0000 2C 01 08 00 ; 鉬??......,...
000000b0h: 00 00 13 01 07 00 00 00 00 00 0000 00 00 00 00 ; ................
000000c0h: 00 00 00 00 36 00 64 00 00 18 0000 00 6C 6F 63 ; ....6.d......loc
000000d0h: 41 47 45 4E 54 5F 53 54 41 54 5553 5F 4D 41 52 ; AGENT_STATUS_MAR
000000e0h: 4B 45 52 28 78 6F 0C 01 16 14 0306 C1 04 1E 37 ; KER(xo......?.7
000000f0h: 59 61 02 C1 41 47 45 4E 54 53 5F4D 41 52 4B 45 ; Ya.罙GENTS_MARKE
00000100h: 44 6F 63 61 30 69 6F 6E 05 02 1900 02 00 FF FF ; Doca0ion......
00000110h: 49 00 80 00 ED 5F 42 01 00 00 0000 02 00 FF FF ; I
00000120h: 04 00 20 00 28 00 15 00 29 33 0000 93 0D 80 00 ; .. .(..
。。。。。。。。。。。。。。。。。。。。。省略。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
其中重要信息如下:
前4个字节为redo标识。
从offset 4起占4字节的是该redoblock的rba:F8 44 01 00。 从offset 12起占1字节是该redoblock中第一个redo record的偏移地址:10(一个字节最多表示到255,因此如果一个redo block中被上一个redo block中的redo record占用超过255字节,则该redo block的剩余空间将不再被使用)。
4、根据上一步骤得到的offset,找到第一个redorecord:90 03 00 00 0D 05 00 00 …………..,其中前4字节90 03 00 00表示该redorecord的长度390,之后的0D是一个叫做VLD的变量,该变量直接决定着redo record header的长度,我一直没有找到该变量和redo record header长度之间的对应关系,但是通过分析得出,最重要的增删改操作(即op为11.*的操作)vld为0D,对应的redo record header长度为68字节,事务相关操作(即op为5.*的操作)vld为05,对应的redo record header长度为68字节。
5、由于该redo record中vld为0D,所以第一个改变向量的物理位置为redo record开始offset 68的位置:
00000054h: 0B 02 01 00 03 00 00 00 E6 66 C000 0F 60 42 01 ; ........鎓?.`B.
00000064h: 00 00 01 00 02 02 73 C4 16 00 1800 31 00 13 00 ; ......s?...1...
00000074h: 07 00 02 00 00 00 00 00 01 000D ; ...........
改变向量中第1、2个字节是该操作的opcode,0B 02转换为10进制为11 2,表示单行插入操作。(op code代码详细信息后附)。 改变向量开始offset 24位置,记录了该改变向量中的记录长度列表:
0000006ch: 16 00 18 00 31 00 13 00 07 00 0200 00 00 00 00 ; ....1...........
0000007ch: 01 00 0D 00 01 00 AB 0C ; ......?
该列表中,每个记录的长度占两个字节存储,第一个长度是指列表本身的长度,列表中的存放的长度数值,指的是对应记录的有效长度,在文件中存储的记录的实际长度要在有效长度的基础上向上增加到4的倍数,比如第1、2字节:16 00转换为10进制为22,所以该列表的长度就是24字节,最后两个字节为填充字节,没有实际意义。
将其中列表转换为10进制,并按4字节补齐后为:
18 00 31 00 13 00 07 00 02 00 00 00 00 0001 00 0D 00 01 00
对应
24 52 20 8 4 0 0 4 16 4
长度列表之后就是记录的实际值:
00000084h: 01 01 00 00 00 00 00 00 05 00 2800 29 33 00 00 ; ..........(.)3..
00000094h: 93 0D 80 00 77 13 31 00 E6 66 C000 E3 66 C0 00 ; ?
000000a4h: FA 12 02 01 01 00 00 00 2C 01 0800 00 00 13 01 ; ?......,.......
000000b4h: 07 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 ; ................
000000c4h: 36 00 64 00 00 18 00 00 00 6C 6F63 41 47 45 4E ; 6.d......locAGEN
000000d4h: 54 5F 53 54 41 54 55 53 5F 4D 4152 4B 45 52 28 ; T_STATUS_MARKER(
000000e4h: 78 6F 0C 01 16 14 03 06 C1 04 1E37 59 61 02 C1 ; xo......?.7Ya.?
000000f4h: 41 47 45 4E 54 53 5F 4D 41 52 4B45 44 6F 63 61 ; AGENTS_MARKEDoca
00000104h: 30 69 6F 6E ; 0ion
这部分中,前24字节对应dump文件中的ktb结构体,offset 24开始的52字节对应dump文件中的kdo结构体,kdo结构体中一个重要的变量就是offset20位置的solt变量,该变量记录了update操作对应的行号,该行号对应的是数据行在data block中的行号,也就是rowid中的行号,至此,已经在redolog中获得了update操作所对应的对象id、dba、行号三个信息,根据此三个信息已经可以计算出redo record操作的行的rowid了。 继续观察,kdo结构体后紧随的是操作的字段编号,占4个字节:01 00 40 04,这里由于前面信息长度列表里这段信息的长度是1,所以有效数据是01,其后的00 40 04为填充位,无意义。再往后保存的是具体操作的字段的值74 65 73 74 31 00 01 00。
SQL> select dump('test1',1016) fromdual;
DUMP('TEST1',1016)
--------------------------------------------------
Typ=96 Len=5 CharacterSet=ZHS16GBK:74,65,73,74,31
可以看到,和dump函数的输出相吻合,最后3个字节的00 01 00,还是填充位,无意义。
这就是一个update操作最关键的修改数据块的改变向量的全部内容,此后在同一redo record中还有其他操作的改变向量,如op为5.1、5.2是对回滚段的相应修改,都属于事务操作的范畴。
Insert操作与update操作的区别是没有“字段编号”这一部分信息(因为用不到),只有ktb、kdo、字段值这三个部分。
Delete操作则只有ktb、kdo两个部分,直接按照kdo中的slot行号信息,加之前的object号,dba就能定位到要删除的行。
至此,redolog中已经能够解出对数据的操作,只要根据不同的op code就能还原redo sql,在这之后又遇到了一个问题,就是如何界定redo log中哪些记录属于一个事务。对此我做了如下实验,在session a中做一个update操作不提交,然后在session b中再做一个update操作并提交,然后再返回session a中提交事务,这样session a中的事务对应的redo 记录就必然被session b中的事务所分割,这个目的是达到了,但是我始终没有找到session a的redo记录之间的联系,session a的事务的改变向量被分割到多个redo record中,没有任何线索能将其串联。为此,一度陷入僵局。
之后我转换了一种思路:这一部分分析的目的,是在恢复数据之后保证数据的准确性,也就是说,要在人工模拟oracle打开过程之后再模拟oracle的recovery database过程。那么,套用oracle本身的行为模式是良好的选择,即,先将redolog里所有对应start scn之后的redo record全部应用前滚,然后再根据data block上的状态flag进行回滚。也就是说oracle本身在redolog这个环节并不需要界定事务,事务的最终标识是在数据块上的相关锁的产生和清除来界定的。 至此redo部分已经可以进行简单的应用,下一步是要根据数据块上的事务标识,通过undo构造一致性块,进一步增加恢复的精确性。
附op code列表(来自网络):
格式:layer: opcode
LAYER的含义:
4 — Block Cleanout
5 — Transaction Management
10 — 索引操作
11 — 行操作
13 — 段[url=]管理[/url]
14 — Extent 管理
17 — 表空间管理
18 — Block Image (Hot Backups)
19 — Direct Loader
20 — Compatibility segment
22 — 本地管理表空间
23 — Block Writes
24 — DDL语句
Layer 1 : Transaction Control - KCOCOTCT
Opcode 1 : KTZFMT
Opcode 2 : KTZRDH
Opcode 3 : KTZARC
Opcode 4 : KTZREP
Layer 2 : Transaction Read - KCOCOTRD
Layer 3 : Transaction Update - KCOCOTUP
Layer 4 : Transaction Block - KCOCOTBK [ktbcts.h]
Opcode 1 : Block Cleanout
Opcode 2 : Physical Cleanout
Opcode 3 : Single Array Change
Opcode 4 : Multiple Changes to an Array
Opcode 5 : Format Block
Layer 5 : Transaction Undo - KCOCOTUN [ktucts.h]
Opcode 1 : Undo block or undo segment header - KTURDB
Opcode 2 : Update rollback segment header - KTURDH
Opcode 3 : Rollout a transaction begin
Opcode 4 : Commit transaction (transaction table update)
- no undo record
Opcode 5 : Create rollback segment (format) - no undo record
Opcode 6 : Rollback record index in an undo block - KTUIRB
Opcode 7 : Begin transaction (transaction table update)
Opcode 8 : Mark transaction as dead
Opcode 9 : Undo routine to rollback the extend of a rollback segment
Opcode 10 :Redo to perform the rollback of extend of rollback segment
to the segment header.
Opcode 11 :Rollback DBA in transaction table entry - KTUBRB
Opcode 12 :Change transaction state (in transaction table entry)
Opcode 13 :Convert rollback segment format (V6 -> V7)
Opcode 14 :Change extent allocation parameters in a rollback segment
Opcode 15 :
Opcode 16 :
Opcode 17 :
Opcode 18 :
Opcode 19 : Transaction start audit log record
Opcode 20 : Transaction continue audit log record
Opcode 24 : Kernel Transaction Undo Relog CHanGe - KTURLGU
Layer 6 : Control File - KCOCODCF [tbs.h]
Layer 10 : INDEX - KCOCODIX [kdi.h]
Opcode 1 : load index block (Loader with direct mode)
Opcode 2 : Insert leaf row
Opcode 3 : Purge leaf row
Opcode 4 : Mark leaf row deleted
Opcode 5 : Restore leaf row (clear leaf delete flags)
Opcode 6 : Lock index block
Opcode 7 : Unlock index block
Opcode 8 : Initialize new leaf block
Opcode 9 : Apply Itl Redo
Opcode 10 :Set leaf block next link
Opcode 11 :Set leaf block previous link
Opcode 12 :Init root block after split
Opcode 13 :Make leaf block empty
Opcode 14 :Restore block before image
Opcode 15 :Branch block row insert
Opcode 16 :Branch block row purge
Opcode 17 :Initialize new branch block
Opcode 18 :Update keydata in row
Opcode 19 :Clear row’s split flag
Opcode 20 :Set row’s split flag
Opcode 21 :General undo above the cache (undo)
Opcode 22 :Undo operation on leaf key above the cache (undo)
Opcode 23 :Restore block to b-tree
Opcode 24 :Shrink ITL (transaction entries)
Opcode 25 :Format root block redo
Opcode 26 :Undo of format root block (undo)
Opcode 27 :Redo for undo of format root block
Opcode 28 :Undo for migrating block
Opcode 29 :Redo for migrating block
Opcode 30 :IOT leaf block nonkey update
Opcode 31 :Cirect load root redo
Opcode 32 :Combine operation for insert and restore rows
Layer 11 : Row Access - KCOCODRW [kdocts.h]
Opcode 1 : Interpret Undo Record (Undo)
Opcode 2 : Insert Row Piece
Opcode 3 : Drop Row Piece
Opcode 4 : Lock Row Piece
Opcode 5 : Update Row Piece
Opcode 6 : Overwrite Row Piece
Opcode 7 : Manipulate First Column (add or delete the 1rst column)
Opcode 8 : Change Forwarding address
Opcode 9 : Change the Cluster Key Index
Opcode 10 :Set Key Links (change the forward & backward key links
on a cluster key)
Opcode 11 :Quick Multi-Insert (ex: insert as select …)
Opcode 12 :Quick Multi-Delete
Opcode 13 :Toggle Block Header flags
Layer 12 : Cluster - KCOCODCL [?]
Layer 13 : Transaction Segment - KCOCOTSG [ktscts.h]
Opcode 1 : Data segment format
Opcode 2 : Merge
Opcode 3 : Set link in block
Opcode 4 : Not used
Opcode 5 : New block (affects segment header)
Opcode 6 : Format block (affects data block)
Opcode 7 : Record link
Opcode 8 : Undo free list (undo)
Opcode 9 : Redo free list head (called as part of undo)
Opcode 9 : Format free list block (freelist group)
Opcode 11 :Format new blocks in free list
Opcode 12 :free list clear
Opcode 13 :free list restore (back) (undo of opcode 12)
Layer 14 : Transaction Extent - KCOCOTEX [kte.h]
Opcode 1 : Add extent to segment
Opcode 2 : Unlock Segment Header
Opcode 3 : Extent DEaLlocation (DEL)
Opcode 4 : Undo to Add extent operation (see opcode 1)
Opcode 5 : Extent Incarnation number increment
Opcode 6 : Lock segment Header
Opcode 7 : Undo to rollback extent deallocation (see opcode 3)
Opcode 8 : Apply Position Update (truncate)
Opcode 9 : Link blocks to Freelist
Opcode 10 :Unlink blocks from Freelist
Opcode 11 :Undo to Apply Position Update (see opcode 8)
Opcode 12 :Convert segment header to 6.2.x type
Layer 15 : Table Space - KCOCOTTS [ktt.h]
Opcode 1 : Format deferred rollback segment header
Opcode 2 : Add deferred rollback record
Opcode 3 : Move to next block
Opcode 4 : Point to next deferred rollback record
Layer 16 : Row Cache - KCOCOQRC
Layer 17 : Recovery (REDO) - KCOCORCV [kcv.h]
Opcode 1 : End Hot Backup : This operation clears the hot backup
in-progress flags in the indicated list of files
Opcode 2 : Enable Thread : This operation creates a redo record
signalling that a thread has been enabled
Opcode 3 : Crash Recovery Marker
Opcode 4 : Resizeable datafiles
Opcode 5 : Tablespace ONline
Opcode 6 : Tablespace OFFline
Opcode 7 : Tablespace ReaD Write
Opcode 8 : Tablespace ReaD Only
Opcode 9 : ADDing datafiles to database
Opcode 10 : Tablespace DRoP
Opcode 11 : Tablespace PitR
Layer 18 : Hot Backup Log Blocks - KCOCOHLB [kcb.h]
Opcode 1 : Log block image
Opcode 2 : Recovery testing
Layer 19 : Direct Loader Log Blocks - KCOCODLB [kcbl.h]
Opcode 1 : Direct block logging
Opcode 2 : Invalidate range
Opcode 3 : Direct block relogging
Opcode 4 : Invalidate range relogging
Layer 20 : Compatibility Segment operations - KCOCOKCK [kck.h]
Opcode 1 : Format compatibility segment - KCKFCS
Opcode 2 : Update compatibility segment - KCKUCS
Layer 21 : LOB segment operations - KCOCOLFS [kdl2.h]
Opcode 1 : Write data into ILOB data block - KDLOPWRI
Layer 22 : Tablespace bitmapped file operations - KCOCOTBF [ktfb.h]
Opcode 1 : format space header - KTFBHFO
Opcode 2 : space header generic redo - KTFBHREDO
Opcode 3 : space header undo - KTFBHUNDO
Opcode 4 : space bitmap block format - KTFBBFO
Opcode 5 : bitmap block generic redo - KTFBBREDO
Layer 23 : write behind logging of blocks - KCOCOLWR [kcbb.h]
Opcode 1 : Dummy block written callback - KCBBLWR
Layer 24 : Logminer related (DDL or OBJV# redo) - KCOCOKRV [krv.h]
Opcode : common portion of the ddl - KRVDDL
Opcode : direct load redo - KRVDLR
Opcode : lob related info - KRVLOB
Opcode : misc info - KRVMISC
Opcode : user info - KRVUSER
备注:该操作不包含undo vector, segment header的修改即使交易回滚,它也不会发生回滚。
|