我们先来看一下大家都耳熟能详的oracle的block的粗略结构:
Typ: Block type Fmt: Block format; in Oracle8, Oracle8i and Oracle9i this is 0x02 Filler: Not currently used RDBA: Relative database address of the block SCNBase: SCN base SCNWrap: SCN wrap Seq: Sequence number incremented for each change made to the block at the same SCN Flg: Flag (as defined in kcbh.h) #define KCBHFNEW 0x01 /* new block - zeroed data area */ #define KCBHFDLC 0x02 /* Delayed Logging Change advance SCN/seq */ #define KCBHFCKV 0x04 /* ChecK Value saved-block xor's to zero */ #define KCBHFTMP 0x08 /* Temporary block */ ChkVal: Optional check value for the block Tail: Information to verify that the beginning and the end of the block are of the same version (lower order 2 bytes of SCNBase, plus block type, plus SCN Seq number) Note: If there are 254 changes at the same SCN, then it is necessary to force a new SCN allocation on the next change of the block. 好了,有了理论基础后我们来分两步构造一个fractured block的例子: SQL> desc t2; Name Type Nullable Default Comments ---- ----------- -------- ------- -------- C1 NUMBER Y C2 VARCHAR2(5) Y SQL> select c1,c2,dbms_rowid.rowid_relative_fno(rowid)||'_'||dbms_rowid.rowid_block_number(rowid) location from t2; C ---------- ----- -------------------------------------------------------------------------------- 2 d 133_34 BBED> set file 133 FILE# 133 BBED> set block 34 BLOCK# 34 BBED> dump File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 0 to 511 Dba:0x21400022 ------------------------------------------------------------------------ 06020000 00080000 00020300 00000000 a9e7d1b7 00000000 00000000 00000000 00000000 00000000 00000000 00010003 ffff0018 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 <32 bytes per line> BBED> set offset 8000 OFFSET 8000 BBED> dump File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8000 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 <32 bytes per line> Oracle在402e中指出: The Tail field corresponds to the last four bytes of the data block, and is used as a sanity check to detect fractured blocks. 现在我们把上述tail改掉,第一步我们只改tail: BBED> set file 133 FILE# 133 BBED> set block 34 BLOCK# 34 BBED> set offset 8188 OFFSET 8188 BBED> dump File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8188 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ d1b70601 <32 bytes per line> BBED> modify /x d1b70602 Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8188 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ d1b70602 <32 bytes per line> BBED> sum apply Check value for File 133, Block 34: current = 0x39df, required = 0x39df 等我改完后再查询t2,oracle这时候如期报错了: SQL> select * from t2; select * from t2 ORA-01578: ORACLE data block corrupted (file # 133, block # 34) ORA-01110: data file 133: '/dras20/astca/test01.dbf' 相应的alert log里显示: *** Corrupt block relative dba: 0x21400022 (file 133, block 34) Fractured block found during buffer read Data in bad block - type: 6 format: 2 rdba: 0x21400022 last change scn: 0x0008.a9e7d1b7 seq: 0x1 flg: 0x06 consistency value in tail: 0xd1b70602 check value in block header: 0x39df, computed block checksum: 0x0 spare1: 0x0, spare2: 0x0, spare3: 0x0 *** Reread of rdba: 0x21400022 (file 133, block 34) found same corrupted data 也就是说oracle这里确实是通过检查tail来决定是否是fractured block。 但是,通常checksum value不对和fractured block的出现不会仅仅是单纯的checksum value或者tail的值不对,往往还伴随着block内部数据的损坏。 这种情况下我们应该怎么办? 好了,我们现在来进行第二步,把表t2中的c1=3的那条记录彻底改坏: BBED> set file 133 FILE# 133 BBED> set block 34 BLOCK# 34 BBED> set offset 8164 OFFSET 8164 BBED> dump File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8164 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ <32 bytes per line> BBED> modify /x 1111111111111111 File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8164 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ 11111111 11111111 <32 bytes per line> BBED> sum apply Check value for File 133, Block 34: current = 0xd7bb, required = 0xd7bb 在这种情况下我们来尝试一下ODU是否能读出其余的c1=1和c1=2的那两条记录: $ export LIBPATH=$ORACLE_HOME/odu/lib:$LIBPATH $ ./odu Oracle Data Unloader:Release Copyright (c) 2008,2009 XiongJun. All rights reserved. Web: http://www.laoxiong.net Email: magic007cn@gmail.com loading default config....... byte_order big block_size 8192 data_path data lob_path lob charset_name ZHS16GBK ncharset_name AL16UTF16 output_format text lob_storage infile clob_byte_order little trace_level 1 delimiter ↑ file_header_offset 0 load control file 'config.txt' successful loading default control file ...... ts# fn rfn bsize blocks bf offset filename ---- ---- ---- ----- -------- -- ------ -------------------------------------------- 0 1 1 8192 64000 N 0 /dras11/oradata/astca/system01.dbf 0 125 125 8192 128000 N 0 /dras21/astca/system02.dbf 84 133 133 8192 128 N 0 /dras20/astca/test01.dbf load control file 'control.txt' successful loading dictionary data...... ODU> unload dict CLUSTER C_USER# file_no: 1 block_no: 89 TABLE OBJ$ file_no: 1 block_no: 121 CLUSTER C_OBJ# file_no: 1 block_no: 25 CLUSTER C_OBJ# file_no: 1 block_no: 25 found found found TABPART$'s obj# 230 found TABPART$'s dataobj#:230,ts#:0,file#:1,block#:1657,tab#:0 found INDPART$'s obj# 234 found INDPART$'s dataobj#:234,ts#:0,file#:1,block#:1689,tab#:0 found TABSUBPART$'s obj# 240 found TABSUBPART$'s dataobj#:240,ts#:0,file#:1,block#:1737,tab#:0 found INDSUBPART$'s obj# 245 found INDSUBPART$'s dataobj#:245,ts#:0,file#:1,block#:1777,tab#:0 found found found LOB$'s obj# 156 found LOB$'s dataobj#:2,ts#:0,file#:1,block#:25,tab#:6 found LOBFRAG$'s obj# 258 found LOBFRAG$'s dataobj#:258,ts#:0,file#:1,block#:1881,tab#:0 ODU> unload table dras.t2 Unloading table: T2,object ID: 86108 Unloading segment,storage(Obj#=86108 DataObj#=86108 TS#=84 File#=133 Block#=33 Cluster=0) ODU> exit $ ls col.odu control.txt ext.odu lib lobfrag.odu obj.odu segment.txt user.odu config.txt data ind.odu lob.odu login.sql odu tab.odu $ cd data $ ls DRAS_T2.ctl ODU_ODU_0000086133.ctl ODU_ODU_0000086133.txt ODU_ODU_0000086134.sql sample.txt DRAS_T2.sql ODU_ODU_0000086133.log ODU_ODU_0000086134.ctl ODU_ODU_0000086134.txt DRAS_T2.txt ODU_ODU_0000086133.sql ODU_ODU_0000086134.log login.sql $ cat DRAS_T2.txt 1↑a 2↑d 熊哥的ODU太牛了,我们从结果来可以看到,我只是随便用了特别旧的2.6的版本(ODU的最新版本是 感觉ODU这里是跳过了它认为有问题的数据行。 我们现在还原c1=3的那条数据,并把c1=2的那条数据改坏。这么做是为了验证ODU在出错后是否会继续unload数据: BBED> set file 133 FILE# 133 BBED> set block 34 BLOCK# 34 BBED> set offset 8164 OFFSET 8164 BBED> dump File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8164 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ 11111111 11111111 <32 bytes per line> BBED> modify /x Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y File: /dras20/astca/test01.dbf (133) Block: 34 Offsets: 8164 to 8191 Dba:0x21400022 ------------------------------------------------------------------------ <32 bytes per line> BBED> sum apply Check value for File 133, Block 34: current = 0xd7bb, required = 0xd7bb ODU> unload table dras.t2 Unloading table: T2,object ID: 86108 Unloading segment,storage(Obj#=86108 DataObj#=86108 TS#=84 File#=133 Block#=33 Cluster=0) ODU> exit $ cd data $ ls DRAS_T2.ctl ODU_ODU_0000086133.ctl ODU_ODU_0000086133.txt ODU_ODU_0000086134.sql sample.txt DRAS_T2.sql ODU_ODU_0000086133.log ODU_ODU_0000086134.ctl ODU_ODU_0000086134.txt DRAS_T2.txt ODU_ODU_0000086133.sql ODU_ODU_0000086134.log login.sql $ cat DRAS_T2.txt 1↑a 3↑c 结论:当碰到fractured block的时候,先用BBED dump一下这个block,看看这个block损坏到什么程度了,然后再用熊哥的ODU来最大限度的抢救数据。
感谢你又给我打了一把广告:)
不用谢:)
06020000 21400022 a9e7d1b7 00080106
Tail应该是scnbase的后两个字节+block type+Seq number
怎么得到d1b7呢?这不是4个字节吗?
d1b70601
谢谢
d1b7是两个字节啊,兄弟。
d1=1101 0001
b7=1011 0111