oracle数据库特种恢复技术(二)—块内篇
作者:谢浩
Oracle数据库特种恢复技术—原理篇链接:http://www.itpub.net/thread-1507760-1-1.html
oracle数据库特种恢复技术—块内篇链接:http://www.itpub.net/thread-1507766-1-1.html
oracle数据库特种恢复技术—转换篇链接:http://www.itpub.net/thread-1507774-1-1.html
BBED> setblock 377
BLOCK# 377Block: 377 Dba:0x00400179
------------------------------------------------------------*** 2011-10-1122:29:03.808
*** SERVICENAME:(SYS$USERS) 2011-10-11 22:29:03.797
*** SESSIONID:(124.22380) 2011-10-11 22:29:03.797
Start dump datablocks tsn: 0 file#: 1 minblk 377 maxblk 377
buffer tsn: 0rdba: 0x00400179 (1/377)
scn:0x0000.0000014a seq: 0x01 flg: 0x04 tail: 0x014a1001
frmt: 0x02chkval: 0xe733 type: 0x10=DATA SEGMENT HEADER - UNLIMITED
Hex dump ofblock: st=0, typ_found=1
Dump of memoryfrom 0x000000000F1DB600 to 0x000000000F1DD600
00F1DB6000000A210 00400179 0000014A 04010000 [....y.a.J......]
…………省略…………..00F1DD5F000000000 00000000 00000000 014A1001 [....... .....J.]
Extent Control Header-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 7last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040017d ext#: 0 blk#: 3 ext size: 7
#blocks in seg. hdr's freelists: 1mapblk 0x00000000 offset: 0
UnlockedMap Header:: next 0x00000000 #extents: 1 obj#: 56 flag: 0x40000000
Extent Map-----------------------------------------------------------------
0x0040017a length: 7
End dump datablocks tsn: 0 file#: 1 minblk 377 maxblk 377
00000000h: 10A2 00 00 79 01 40 00 4A 01 00 00 00 00 01 04 ; .?.y.a.J.. ....
00000010h: 33E7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 3?.. ..... ....
00000020h: 0000 00 00 01 00 00 00 07 00 00 00 20 10 00 00 ; .. ..... ... ...
00000030h: 0000 00 00 03 00 00 00 07 00 00 00 7D 01 40 00 ; .. ..... ...}.a.
00000040h: 0000 00 00 00 00 00 00 01 00 00 00 03 00 00 00 ; .. ..... .......
00000050h: 0000 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ; .. ..... .......
00000060h: 0000 00 00 38 00 00 00 00 00 00 40 7A 01 40 00 ; ....8.. ...az.a.
00000070h: 0700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
00000080h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
00000090h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
000000a0h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
…………………省略……………………..377块的block type为0x10,表示该块是Data segment header block (unlimited extents, no freelist groups),就是mssm下的segment header,而assm的segment header的block type为0x24,表示PAGETABLEEXTENT MAP BLOCK(bbed工具目前不支持assm相关的segment header和三级位图块)。
结合观察三种工具的输出。bbed中的“structktetb[1], 8 bytes a108” 对应的就是oracle dump输出的extent map,该结构体记录了segment中的extent的首地址。这里有一点特别的地方,在进行分析的过程中发现,存放在assm表空间中的segment的header block中的extent map包涵了三级位图的块,就是说assm管理的下的extent map覆盖整个segment占用的块,而存放在mssm表空间中的segment的header block中的extent map,仅包涵segment中存放数据的块,没有包涵段头块本身。由于sys.bootstrap$表处于system表空间中,因此采用的是mssm的管理方式。
Assm与mssm下的extent map格式是相同的,每个extent的信息占用8字节存储,前4字节记录该extent中的第一个块的dba(data block address),后4字节记录该extent包涵的块的个数,使用bbed对其进行观察:BBED> pktetb
structktetb[0], 8 bytes a108
ub4 ktetbdba a108 0x0040017a
ub4 ktetbnbk a112 0x00000007Block: 377 Dba:0x00400179
------------------------------------------------------------ub4 count_ktemh a92
ub4 next_ktemh a96BBED> setblock 378
BLOCK# 378Block: 378 Dba:0x0040017a
------------------------------------------------------------oracledump输出:
Start dump data blocks tsn: 0 file#: 1minblk 378 maxblk 378
buffer tsn: 0 rdba: 0x0040017a (1/378)
scn: 0x0000.0000015d seq: 0x01 flg:0x06 tail: 0x015d0601
frmt: 0x02 chkval: 0x13d5 type:0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x000000001E849600to 0x000000001E84B600
01E849600 0000A206 0040017A 0000015D06010000 [....z.a.].. ....]
01E849610 000013D5 00000001 00000038000000C7 [.. .....8.. ....]
01E849620 00000000 00020001 0000000000220000 [.. ..... ,...".],
01E849630 00000002 00400182 000C000200002018 [.. ...a.. ... ..]
……………………………省略部分内容………………………
Block header dump: 0x0040017a
Object id on Block? Y
seg/obj: 0x38 csc: 0x00.c7 itc: 1 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.022.00000002 0x00400182.0002.0c --U- 24 fsc 0x0000.0000015d
data_block_dump,data header at0x1e849644
===============
tsiz: 0x1fb8
hsiz: 0x42
pbl: 0x1e849644
bdba: 0x0040017a
76543210
flag=--------
ntab=1
nrow=24
frre=-1
fsbo=0x42
fseo=0x4c8
avsp=0x486
tosp=0x486
0xe:pti[0] nrow=24 ffs=0
0x12:pri[0] offs=0x1fa3
0x14:pri[1] offs=0x1f1a
0x16:pri[2] offs=0x1d95
0x18:pri[3] offs=0x1ccd
0x1a:pri[4] offs=0x1b4e
0x1c:pri[5] offs=0x1a7a
0x1e:pri[6] offs=0x19ad
0x20:pri[7] offs=0x1749
0x22:pri[8] offs=0x167b
0x24:pri[9] offs=0x15b3
0x26:pri[10] offs=0x14d6
0x28:pri[11] offs=0x140a
0x2a:pri[12] offs=0x12ef
0x2c:pri[13] offs=0x1205
0x2e:pri[14] offs=0x110e
0x30:pri[15] offs=0xf38
0x32:pri[16] offs=0xe68
0x34:pri[17] offs=0xd91
0x36:pri[18] offs=0xc79
0x38:pri[19] offs=0x969
0x3a:pri[20] offs=0x89c
0x3c:pri[21] offs=0x65e
0x3e:pri[22] offs=0x58e
0x40:pri[23] offs=0x4c8
block_row_dump:
tab 0, row 0, a0x1fa3
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 3] 3e 64 66
col 1: [ 3] 3e 64 66
col 2: [ 9] 38 2e 30 2e 30 2e 30 2e30
tab 0, row 1, a0x1f1a
tl: 137 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 1] 80
col 1: [ 1] 80
col 2: [129]
43 52 45 41 54 45 20 52 4f 4c 4c 42 41 43 4b20 53 45 47 4d 45 4e 54 20 53
59 53 54 45 4d 20 53 54 4f 52 41 47 45 20 2820 20 49 4e 49 54 49 41 4c 20
31 31 32 4b 20 4e 45 58 54 20 31 30 32 34 4b20 4d 49 4e 45 58 54 45 4e 54
53 20 31 20 4d 41 58 45 58 54 45 4e 54 53 2033 32 37 36 35 20 4f 42 4a 4e
4f 20 30 20 45 58 54 45 4e 54 53 20 28 46 494c 45 20 31 20 42 4c 4f 43 4b
20 39 29 29
……………………………..省略………………………
在378号块中,从structkdbh, 14 bytes a68开始记录的就是块内数据行的相关字典信息,kdbh数据结构的偏移量为:20+24+ ktbbh. Ktbbhict*24。具体含义为kcbh结构体20字节+ktbbh结构体中的固定长度部分24字节+ktbbh中ktbbhitl数组长度(每个itl槽占24字节,itl槽个数记录在ktbbhict变量中)。注:这个偏移量公式只适用于mssm表空间,assm稍有区别,后详述。
继续观察kdbh结构体:struct kdbh, 14bytes a68
ub1 kdbhflag a68 0x00 (NONE)00000000h: 06 A200 00 7A 01 40 00 5D 01 00 00 00 00 01 06 ; .?.z.a.].. ....
00000010h: D5 1300 00 01 00 00 00 38 00 00 00 C7 00 00 00 ; ?.. ...8...?..
00000020h: 00 0000 00 01 00 02 00 00 00 00 00 00 00 22 00 ; .. ..... .....".
00000030h: 02 0000 00 82 01 40 00 02 00 0C 00 18 20 00 00 ; ....?a.. ... ..
00000040h: 5D 0100 00 00 01 18 00 FF FF 42 00 C8 04 86 04 ; ].. ....??B.??
00000050h: 86 0400 00 18 00 A3 1F 1A1F 95 1D CD 1C 4E 1B ; ?....?..??N.
00000060h: 7A 1AAD 19 49 17 7B 16 B3 15 D6 14 0A 14 EF 12 ; z.?I.{.??..?
00000070h: 05 120E 11 38 0F 68 0E 91 0D 79 0C 69 09 9C 08 ; ....8.h.?y.i.?
00000080h: 5E 068E 05 C8 04 00 00 00 00 00 00 00 00 00 00 ; ^.??.. .......
00000090h: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
000000a0h: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......
上面红色白底标注的就是378块offset 86位置。我进行分析所用数据库安装在linux操作系统上,linux操作系统采用Little Endian存储模式,因此一个变量中的各个字节之间的顺序和正常阅读顺序是颠倒的,因此该位置的值是1F A3,与dump文件中行列表中的第一行相吻合。00001f90h: 49 4E49 54 49 41 4C 20 31 31 32 4B 20 4E 45 58 ; INITIAL 112K NEX
00001fa0h: 54 2031 30 32 34 4B 20 4D 49 4E 45 58 54 45 4E ; T 1024K MINEXTEN
00001fb0h: 54 5320 31 20 4D 41 58 45 58 54 45 4E 54 53 20 ; TS 1 MAXEXTENTS
00001fc0h: 33 3237 36 35 20 4F 42 4A 4E 4F 20 30 20 45 58 ; 32765 OBJNO 0 EX
00001fd0h: 54 454E 54 53 20 28 46 49 4C 45 20 31 20 42 4C ; TENTS (FILE 1 BL
00001fe0h: 4F 434B 20 39 29 29 2C 01 03 03 3E 64 66 03 3E ; OCK9)),...>df.>
00001ff0h: 6466 09 38 2E 30 2E 30 2E 30 2E 30 01 06 5D 01 ; df.8.0.0.0.0..].
2C01 03 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30
这一行字符,就是该表第一行的全部数据。第一个字节的2C表示该行的row flag,采用的是符号位的存储方式,即该字节的8个比特位分别代表块的一种属性,该比特位为1表示该属性对于该块成立。2C对应为00101100与dump文件中该行的flag相吻合:2C 0103 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30
第二个字节的01表示行上的锁的状态。2C 01 0303 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30
第三个字节的03表示行内字段的数量,这里要注意的是flag、lock信息、column count三中信息不一定固定为三个字节,在簇表等其他情况下,可能是3字节或者9字节或者19字节等等不同长度,需要针对segment类型分别分析。2C 01 03 033E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30
第四个字节的03表示之后这个字段(行内存储的第一个字段)占用3个字节存储。提取后面三个字节的内容“3E 64 66”,与sys.bootstrap$表中第一行的第一个字段的值“-1”吻合:CREATE TABLE SEG$("FILE#" NUMBER NOTNULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBER NOTNULL,"TS#" NUMBER NOT NULL,"BLOCKS" NUMBER NOTNULL,"EXTENTS" NUMBER NOT NULL,"INIEXTS" NUMBER NOTNULL,"MINEXTS" NUMBER NOT NULL,"MAXEXTS" NUMBER NOTNULL,"EXTSIZE" NUMBER NOT NULL,"EXTPCT" NUMBER NOTNULL,"USER#" NUMBER NOT NULL,"LISTS"NUMBER,"GROUPS" NUMBER,"BITMAPRANGES" NUMBER NOTNULL,"CACHEHINT" NUMBER NOT NULL,"SCANHINT" NUMBER NOTNULL,"HWMINCR" NUMBER NOT NULL,"SPARE1"NUMBER,"SPARE2" NUMBER) STORAGE ( OBJNO 14 TABNO 2) CLUSTER C_FILE#_BLOCK#(TS#,FILE#,BLOCK#)
通过红色字体白底部分,可见该表属于C_FILE#_BLOCK#簇,而C_FILE#_BLOCK#簇在sys.bootstrap$中的定义为:CREATE CLUSTERC_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#"NUMBER,"SEGBLOCK#" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS255 STORAGE ( INITIAL 24K NEXT 1024KMINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO 8 EXTENTS (FILE 1 BLOCK73)) SIZE 225
可见,该簇的segment header block存放在file 1的73号block中。其中extent map的第一行指向file 1的74号block。对该block进行观察:BBED> setblock 74
BLOCK# 74Block: 74 Dba:0x0040004a
------------------------------------------------------------File: /home/oracle/vie.pdf(1)
Block: 74 Dba:0x0040004a
------------------------------------------------------------KTB Data Block(Table/Cluster)
struct kcbh, 20 bytes a000001fe0h: AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00; ?.. ...a.J...a.
00001ff0h: 4A 00 00 01 80 02 C1 02 03 C2 04 26 0106 FE 98 ;
块最后4个字节是校验信息,红色部分就是第一行数据,逐个字节对其进行分析:第二个字节表示该行的锁信息。
AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 8002 C1 02 03 C2 04 26
tl: 28 fb: K-H-FL-- lb: 0x0 cc: 3
curc: 1 comc: 1 pk:0x0040004a.0 nk: 0x0040004a.0
col 0: [ 1] 80AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 0000 01 80 02 C1 02 03 C2 04 26
第六、七两个字节对应dump文件中的comc的值:tl: 28 fb: K-H-FL-- lb: 0x0 cc: 3
curc: 1 comc: 1 pk: 0x0040004a.0 nk: 0x0040004a.0AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 004A 00 00 01 80 02 C1 02 03 C2 04 26
八到十三字节记录了簇中具有该key值的前一行的存储位置,对应dump文件中的pk的值,其存储格式为,data block address.row index,表示具有这个key的值的上一行数据的存储位置,这里看到这个值是指向该行自身的,说明本行数据是簇中该key对应的第一行数据:curc: 1 comc: 1 pk:0x0040004a.0 nk: 0x0040004a.0
col 0: [ 1] 80
col 1: [ 2] c1 02AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 0000 01 80 02 C1 02 03 C2 04 26
十四到十九字节记录了簇中具有该key值的后一行的存储位置,对应dump文件中的nk的值,其存储格式为,data block address.row index,表示具有这个key的值的下一行数据的存储位置,这里看到这个值是指向该行自身的,说明本行数据是簇中该key对应的最后一行数据:tl: 28 fb: K-H-FL-- lb:0x0 cc: 3
curc: 1 comc: 1pk: 0x0040004a.0 nk: 0x0040004a.0
col 0: [ 1] 80
col 1: [ 2] c1 02AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 0180 02 C1 02 03 C2 04 26
下一个字节01表示第一个字段占一个字节存储。AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 8002 C1 02 03 C2 04 26
80是第一个字段的值0的oracle内部表示方法。AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02C1 02 03 C2 04 26
下一个字节02表示第二个字段占两个字节存储。AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26
下两个字节C1 02是数字1的oracle内部表示方法。AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26
下一个字节的03表示第三个字段占三个字节存储。AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26
最后三个字节C2 04 26是数字337的oracle内部表示方法。1、 在segment header block偏移量96的位置找到ktemh结构体的next_ktemh变量,该变量指向segment的下一个extent map。如果该变量的值为0x00000000,表示该segment的所有数据都存储在一个segment中。
2、 在segment header block偏移量108的位置找到ktetb数组,该数组中每个节点占8字节,前4字节为extent首地址,后4字节为extent内块的个数。
3、 通过extent map找到具体存储数据的块。
4、 在数据块内找到kdbh数据结构,其偏移量为:20+24+ktbbh. Ktbbhict(块内偏移量36位置,长度两字节)*24。
5、 找到kdbr数组,其偏移量为:kdbh的偏移量+14(kdbh结构体的长度)+kdbh结构体中kdbhntab变量的值*4(每个表占4字节)。
6、 取出kdbr中行的偏移量,将该值加上kdbh的偏移量值,得到行物理偏移量。
7、 根据行物理偏移量,找到行数据起始位置,并取出其首字节flag。
根据块的flag判断行头的不同长度。如,普通无行链接数据行,flag为2C对应为二进制00101100 --H-FL—则行内数据位于行头偏移3字节的位置。簇且无行链接数据行,flag为AC对应二进制10101100 K-H-FL—则行内数据位于行头偏移19字节的位置。8、 在行头第三个字段,取出行内字段个数。
9、 在行头后找到首个字段的长度,并按该长度取出其后的字段值。
10、 按步骤9递归取出行内全部字段的值。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9606200/viewspace-745678/ ,如需转载,请注明出处,否则将追究法律责任。