接上文《Oracle压缩表数据块格式解析 PartI》,本文将分析压缩表块中实际的行数据部分。

首先我们还是先来看看行数据部分(tab 1)序号为0的行的数据:

tab 1, row 0, @0x1855
tl: 52 fb: --H-FL-- lb: 0x0  cc: 13
col  0: [ 3]  53 59 53
col  1: *NULL*
col  2: [ 5]  56 41 4c 49 44
col  3: [ 1]  4e
col  4: [ 1]  4e
col  5: [ 1]  4e
col  6: [ 2]  c1 62
col  7: [ 5]  54 41 42 4c 45
col  8: [ 7]  78 6d 03 0f 12 2a 38
col  9: [ 7]  78 6d 03 0f 12 2a 38
col 10: [19]  32 30 30 39 2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35
col 11: [ 7]  41 43 43 45 53 53 24
col 12: [ 2]  c1 62
bindmp: 2c 00 0d 06 76 c9 78 79 79 79 ca c1 62 cd 54 41 42 4c 45 77 77 db 32 30 30 39 2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35 cf 41 43 43 45 53 53 24 ca c1 62

bindmp那一行,与符号表中的一样,都是表示这一行数据在数据块中的原始数据,也就是压缩后的数据。我们需要把这个数据解压还原。

bindmp: 2c 00 0d 06 76 c9 78 79 79 79 ca c1 62 cd 54 41 42 4c 45 77 77 db 32 30 30 39
        2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35 cf 41 43 43 45 53 53 24 ca c1 62

为了便于排版,这里把数据处理后显示为2行。

2c 行标志字节,与普通表数据块一样
00 锁标志,即ITL插槽位置
0d 这个字节表示数据被压缩后的列数,可以理解为后面的数据被分割成了几部分。
06 表示这一行中前面6列数据一定是被压缩的,具体含义在后面可以进一步解析。这一个字节,是网上很多讲压缩表格式时都没有正确地给出其意义的。
接下来的部分,与符号表类型,都是
    标志字节+数据
76 表示数据来自符号表中的序号为118(0x76)的行。
c9 依照解析符号表时的推论,应该表示接紧着后面的数据是实际的列数据,长度为1。但实际上在这里更为复杂。这里也是网上所有关于压缩表块格式分析的文章没有提到的。上面提到过”06 表示这一行中前面6列数据一定是被压缩的“,由于到这列为止,只压缩了1列,因此还没有到6列,那么这1列也是被压缩的,只不过压缩的数据没有在符号表中,而在这个字节紧接着的后面。

我们来看看上面提到的"C9”究竟代表什么?这里就不得不引入了压缩块头部中的fcls_9ir2:

Read the rest of this entry

,

前段时间花了点时间研究了一下Oracle压缩表的数据块格式,并给ODU增加了压缩表支持功能。鉴于目前在网络上基本上没有完整的关于Oracle压缩表数据块的格式分析,我觉得有必要把自己的研究得到的知识,发布出来与大家分享。

由于Oracle 9i与Oracle 10g在压缩块格式上没啥区别,我使用ODU也能够在11g上导出压缩表,所以本文就是Windows上的Oracle 9.2.0.8下的压缩表来进行块格式分析。下面我们用DBA_OBJECTS的数据建一个测试表:

SQL> create table t1 as select * from dba_objects;

表已创建。
SQL> select header_file,header_block from dba_segments where owner=user and segment_name='T1';

HEADER_FILE HEADER_BLOCK
----------- ------------
          8          137
SQL> alter system dump datafile 8 block 138;

系统已更改。

由于T1表所在的表空间是MSSM管理方式的表空间,一般来说紧接着段头后面的数据块就是存储表实际数据的第1个块。所以dump出了datafile 8的138块。

下面从块头往下一直进行解析。

Start dump data blocks tsn: 8 file#: 8 minblk 138 maxblk 138
buffer tsn: 8 rdba: 0x0200008a (8/138)
scn: 0x0000.003d0735 seq: 0x02 flg: 0x04 tail: 0x07350602
frmt: 0x02 chkval: 0xe80a type: 0x06=trans data
Block header dump: 0x0200008a
Object id on Block? Y
seg/obj: 0x1be4 csc: 0x00.3d0735 itc: 3 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0xffff.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.003d0735
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
0x03   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

这一部分是Oracle块的头部,与其他非压缩表块的头部没什么不同。接下来是表类型(相对于索引类型的数据块)的数据头部:

data_block_dump,data header at 0x3393074
===============
tsiz: 0x1f88
hsiz: 0x280
pbl: 0x03393074
bdba: 0x0200008a
     76543210
flag=-0------  这里“O”标记表示是压缩的块
ntab=2     这里说明块里面有两个“表”的数据,实际上是“符号表”和“实际的表数据”,分别是"tab 0"和"tab 1"
nrow=291
frre=-1
fsbo=0x280
fseo=0x28c
avsp=0xc
tosp=0xc
  r0_9ir2=0x0        这里及下面的数行都是压缩表特有的数据
  mec_kdbh9ir2=0x3  这个字段后面有详细说明
  r1_9ir2=0x0
            76543210
  flag_9ir2=------OC
    fcls_9ir2[7]={ 0 32768 32768 32768 32768 32768 32768 }  这个字段后面有详细说明
    perm_9ir2[13]={ 0 11 1 12 6 7 8 9 10 2 3 4 5 }  这个字段后面有详细说明
0x32:pti[0] nrow=122 offs=0
0x36:pti[1] nrow=169 offs=122
0x3a:pri[0] offs=0x189f
0x3c:pri[1] offs=0x1890
0x3e:pri[2] offs=0x1889
0x40:pri[3] offs=0x18ae
0x42:pri[4] offs=0x18c4
0x44:pri[5] offs=0x18ce

行目录数据比较多,在此大部分省略。接下来,就是tab 0,也就是符号表的数据了:

Read the rest of this entry

,

Oracle数据文件的坏块,可分为物理坏块和逻辑坏块。物理坏块(也可以称为介质坏块)指的是块格式本身是坏的,块内的数据没有任何意义。而逻辑坏块,指的是块内的数据在逻辑是存在问题。比如说索引块的索引值没有按从小到大排列。物理坏块一般是由于内存问题、OS问题、IO子系统问题和硬件引起,逻辑坏块一般是是由于Oracle Bug等原因引起。

Oracle数据文件的每个块,其块头为20字节。其定义如下:(来自于DSI401)

struct kcbh
{
    ub1 type_kcbh; /* block type */
    ub2 frmt_kcbh; 
    ub1 spare1_kcbh;
    ub1 spare2_kcbh;
    krdba rdba_kcbh; /* relative DBA */
    ub4 bas_kcbh; /* base of SCN */
    ub2 wrp_kcbh; /* wrap of SCN */
    ub1 seq_kcbh; /* sequence # of changes at the same scn */
    ub1 flg_kcbh; 
    ub2 chkval_kcbh;
};  
  

在块头中,seq_kcbh(占用1字节,块头偏移14)有着特殊的含义,如果该值为0xff,则表示该块被标记为corruption

下面我们做一个测试:

SQL> create table test.t1 as select * from dba_objects;

表已创建。

SQL> select header_file,header_block from dba_segments where segment_name='T1' and owner='TEST';

HEADER_FILE HEADER_BLOCK
----------- ------------
         10         1445

修改db_block_checksum参数值为TRUE,关闭数据库,我们用ultraedit修改10号文件的1447块的check sum(一个随便>0的数)及flag=0x04。然后再打开数据库。再执行下面的查询:

SQL> select count(*) from test.t1;
select count(*) from test.t1
*
ERROR 位于第 1 行:
ORA-01578: ORACLE 数据块损坏(文件号10,块号1447)
ORA-01110: 数据文件 10: 'D:\ORACLE\ORADATA\XJ\TEST01.DBF'

由于非系统表空间在db_block_checksum参数设为FALSE时,会忽略checksum的检查。所以这里为了测试的方便设置为TRUE。
从上面的错误信息来看,块号1447这个块已经坏了,报的错误是经典的ORA-01578错误。

Read the rest of this entry

,