接上文《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:

fcls_9ir2[7]={ 0 32768 32768 32768 32768 32768 32768 }

这是一个数组,如果我们把数组中的每个值与0x7FFFF按位AND操作,也就是去掉最高位,就是:

fcls_9ir2[7]={ 0 0 0 0 0 0 0}

下面再来解析这个“C9”代表的意义:
0xC9=201,201-200=1,这里结果为1表示C9这个字节后面被压缩了1列数据。当然如果是CB就表示压缩了3列数据。

这里的压缩,实际上只是省略了列的长度。那么列的长度从何得知?答案就在"fcls_9ir2"。依据下面的公式:
    设当前列序号为n,n从0开始计:
    列长度=fcls_9ir2[n+1]-fcls_9ir2[n]
比如C9表示的列数1列,列序号为1,那么列长度=fcls_9ir2[2]-fcls_9ir2[1]=0,也就是这列值为NULL

再举另一个例子,这个例子的数据来源于datafile 8 block 140:

fcls_9ir2[12]={ 0 32768 32768 32768 32768 32768 32768 32768 32768 7 14 33 }

去掉fcls_9ir2中数据的最高位,结果为

fcls_9ir2[12]={ 0 0 0 0 0 0 0 0 0 7 14 33 }

我们来看这个块中的bindmp:

bindmp: 2c 00 05 0b 04 c9 78 6d 03 0f 12 2b 2f 73 d8 44 42 41 5f 41
        53 53 4f 43 49 41 54 49 4f 4e 53 cb c2 15 5e

"2c 00 05 0b 04 c9",这里的c9同样是表示后面的是压缩数据,列数为1。只不过当前列序号n=9,因此代入公式:
    列长度=fcls_9ir2[10]-fcls_9ir2[9]=14-7=7,因此这个列的长度即为7。也就是这一列值为"78 6d 03 0f 12 2b 2f"

让我们回头继续解析本文最早的那个压缩数据bindmp:

78 79 79 79 指向符号表中的120(0x78)行、121(0x79)、121、121行,这4行共4列数据。
ca c1 62 前面解析出的压缩数据列数,已经有6列,因此这里的ca不再有特殊的含义,而是与符号表中的一样,表示本列有2字节数据,是c1 62。
cd 54 41 42 4c 45 cd表示5字节数据,为54 41 42 4c 45
77 77 指向符号表中的119行,这2行共2列数据。
db 32 30 30 39 2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35 表示长度为19字节的1列数据。
cf 41 43 43 45 53 53 24 表示长度为7字节的1列数据。
ca c1 62 表示长度为2字节的1列数据。

至此,压缩后的数据,已经解了13(0x0d)项,与bindmp中的第3个字节"0d"相等,表示此行解压缩已经完成。注意这一行的列数也是13列,只不过是遇巧而已,压缩比率很高的行,在压缩后只有5部分(项)。

为了能够具有更高的压缩比率,Oracle在压缩块时,对列在块中的存储顺序可能做了调整,使得块中的数据其列序号与数据字典不再对应。数据块头部的perm_9ir2数组记录了数据列在块中实际排列的顺序。

perm_9ir2[13]={ 0 11 1 12 6 7 8 9 10 2 3 4 5 }

假设数据字典中的列序号为n,那么对于列n,在压缩的块解压后的行数据中,实际存储该列值的列序号m为:

m=perm_9ir2[n]

对于这个表,表的各个列为:

名称               是否为空? 类型
------------------ -------- -------------
OWNER                       VARCHAR2(30)
OBJECT_NAME                 VARCHAR2(128)
SUBOBJECT_NAME              VARCHAR2(30)
OBJECT_ID                   NUMBER
DATA_OBJECT_ID              NUMBER
OBJECT_TYPE                 VARCHAR2(18)
CREATED                     DATE
LAST_DDL_TIME               DATE
TIMESTAMP                   VARCHAR2(19)
STATUS                      VARCHAR2(7)
TEMPORARY                   VARCHAR2(1)
GENERATED                   VARCHAR2(1)
SECONDARY                   VARCHAR2(1)

那么,可以得到:OWNER列在块中实际的列序号为perm_9ir2[0]=0,OBJECT_NAME列在块中实际的列序号为perm_9ir2[1]=11,以此类推。

本文对Oracle压缩表数据块格式的解析,经过数据验证的范围有限,因此有可能存在错误,也欢迎发现错误的朋友及时提出。

本文解析所用的块dump出来的文件,点击此处下载

,
Trackback

7 comments untill now

  1. valen wong @ 2010-01-15 00:32

    熊兄:
    我看这里和我的观察的结果不一样呢?
    —你提到———————————————————
    下面再来解析这个“C9”代表的意义:
    0xC9=201,201-200=1,这里结果为1表示C9这个字节后面被压缩了1列数据。当然如果是CB就表示压缩了3列数据。
    ————————————————————
    我的dump如下
    col 0: *NULL*
    col 1: [ 5] 56 41 4c 49 44
    col 2: [ 1] 4e
    col 3: [ 1] 4e
    col 4: [ 1] 4e
    col 5: [ 3] 53 59 53
    col 6: [ 7] 78 6c 03 0e 13 2f 2f
    col 7: [19] 32 30 30 38 2d 30 33 2d 31 34 3a 31 38 3a 34 36 3a 34 36
    col 8: [ 5] 49 4e 44 45 58
    col 9: [ 7] 78 6c 03 0e 13 2f 2f
    col 10: [ 2] c1 2d
    col 11: [ 7] 49 5f 55 53 45 52 31
    col 12: [ 2] c1 2d
    bindmp: 2c 00 04 05 03 ca c1 2d cf 49 5f 55 53 45 52 31 ca c1 2d
    2c 标志
    00 lock
    04 分为4个部分
    05 前5行是被压缩的
    03 指向元数据表的03行,03行有10列
    ca ca=202 202-200=2 ,表示接下来的2个byte是字段c1 2d
    cf 49 5f 55 53 45 52 31 207-200=7, 后面字段长度7
    ca c1 2d 202-200=2
    组合下来,刚好完美13列,吻合。
    =======
    那么这些个ca cf ca就直接表示的字段长度,而不像你说是包含几个字段,而不需要去和前面的fcls_9ir2数组做什么计算,就可以得到了。
    我的数据库是10.2.0.4 win32。

    你看这里是不是这样? 还是我搞错了。

    ————————————
    我也是成都的dba, 赏脸加msn,认识一下吧?

    Valen wong
    Regards

    老熊 Reply:

    @valen wong, 我举的是一种特殊的情况了。你这里05表示是说有“至少5列是被压缩的”,不代表只被压缩了5列。同时,你这里实际压缩了10列,大于5列,所以ca开始的列都没有被压缩,也就不需要对照fcls_9ir2数组了。假如你这里03指向的符号表中,解压出来只有4列,少于压缩的列数“5″,就需要参考那个数组了。你这种情况是大多数的情况,也是网上其他文章通常举出的例子,而我列出的,更特殊一点,也是网上其他文章没有提到的。

    很高兴认识你。我已加了你的MSN。

  2. valen wong @ 2010-01-31 17:23

    非常感谢老熊指点, 我还费了劲才完全弄明白这个东西。
    压缩块在oracle数据块里面还真复杂的了,比cluster都复杂。
    按你的思路,我写个了小程序把块读出来
    还真的正确!
    。。。。
    Row 0 :(0),(0),(5)56414C4944,(1)4E,(1)4E,(1)4E,(6)5055424C4943,(7)53594E4F4E594D,
    Row 1 :(R0),(R34),(R33),(R34),
    Row 2 :(R0),(R31),(R32),(R31),
    Row 3 :(R0),(R29),(R30),(R29),
    Row 4 :(R0),(R27),(R28),(R27),

    Row 0 :(offset 7746)2C(3parts)(compress11)(0),(0),(5)56414C4944,(1)4E,(1)4E,(1)4E,(6)5055424C4943,(7)53594E4F4E594D,(7)786C030E13301D,(19)323030382D30332D31343A31383A34373A3238,(7)786C030E13301D,(10),(3),

    。。。。。

    Row 1 :(offset 7725)2C(3parts)(compress11)(0),(0),(5)56414C4944,(1)4E,(1)4E,(1)4E,(3)535953,(4)56494557,(7)786C030E13301D,(19)323030382D30332D31343A31383A34373A3238,(7)786C030E13301D,(11),(3),
    Row 2 :(offset 7705)2C(3parts)(compress11)(0),(0),(5)56414C4944,(1)4E,(1)4E,(1)4E,(6)5055424C4943,(7)53594E4F4E594D,(7)786C030E13301D,(19)323030382D30332D31343A31383A34373A3238,(7)786C030E13301D,(10),(3),

    老熊 Reply:

    @valen wong, good

  3. bindmp: 2c 00 05 0b 04 c9 78 6d 03 0f 12 2b 2f 73 d8 44 42 41 5f 41
    53 53 4f 43 49 41 54 49 4f 4e 53 cb c2 15 5e

    “2c 00 05 0b 04 c9″,这里的c9同样是表示后面的是压缩数据,列数为1。只不过当前列序号n=9,因此代入公式:
    列长度=fcls_9ir2[10]-fcls_9ir2[9]=14-7=7,因此这个列的长度即为7。也就是这一列值为”78 6d 03 0f 12 2b 2f”

    这里的n=9是当前列序号,从哪里看出来的?能附一个完整一点的dump吗?谢谢先………………

  4. […] 1讲了一般情况下的压缩表存放格式,下面说下另一种情况。 这里老熊讲过Oracle压缩表数据块格式解析 PartII 这里看下老熊例子 我们来看这个块中的bindmp: tl: 35 fb: –H-FL– lb: 0x0 cc: 13 […]

  5. 熊哥,我遇到一个不一致的情况,10g上:
    tab 1, row 134, @0x1098
    tl: 54 fb: –H-FL– lb: 0x0 cc: 13
    col 0: *NULL*
    col 1: [ 5] 56 41 4c 49 44
    col 2: [ 1] 4e
    col 3: [ 1] 4e
    col 4: [ 1] 59
    col 5: [ 5] 4f 55 54 4c 4e
    col 6: [ 7] 78 6e 04 13 0b 17 35
    col 7: [19] 32 30 31 30 2d 30 34 2d 31 39 3a 31 30 3a 32 32 3a 35 32
    col 8: [ 3] 4c 4f 42
    col 9: [ 7] 78 6e 04 13 0b 17 35
    col 10: [ 3] c2 05 37
    col 11: [25]
    53 59 53 5f 4c 4f 42 30 30 30 30 30 30 30 34 35 33 43 30 30 30 32 31 24 24
    col 12: [ 3] c2 05 37
    bindmp: 2c 00 0d 05 c9 30 31 31 c9 59 cd 4f 55 54 4c 4e 26 21 23 26 cb c2 05 37 e1 53 59 53 5f 4c 4f 42 30 30 30 30 30 30 30 34 35 33 43 30 30 30 32 31 24 24 cb c2 05 37

    按照你的说法,应该前5列都是被压缩的,第二个C9应该也是null,结果对应却是59,能解释下么?

Add your comment now