简单了解一下压缩表

数据被压缩的原理在于,对于数据块里的重复值来说,只保留一份。保存在数据块里的压缩数据是自我包含的。也就是说,对于将压缩的数据进行解压来说,所有需要的信息都在数据块里了,而不需要参考数据块以外的其他数据。

数据块里的所有重复数据行和数据列都只存放一次,并放在数据块的开始部分,这块区域叫做数据块的symbol table。也就是说,同一个数据块里放了两个表的数据。这一点有点像class table。数据行里如果存在重复的值,则该重复值去掉,并在原来的位置上放了一个很短的引用,指向symbol table。除了数据块开始部分的symbol table以外,压缩数据块与普通的数据块没什么两样。注意,在9i里,压缩表在创建以后就不能修改结构的,而10g则没有这个限制了,但是不能对添加的列提供缺省值。

 

对于压缩数据块的访问来说,并没有很消耗资源的解压缩操作。这也就是说,在考虑是否要启用压缩时,并不需要考虑对于查询性能的影响。只能在进行批量数据加载的时候,完成压缩操作。在加载数据并压缩过程中,CPU资源的消耗会有50%的增长。

 

只有当数据批量插入或加载的时候完成压缩,这些操作包括:

·SQL*Loader的直接路径加载。

·CREATE TABLE COMPRESS AS SELECT

·并行insert或带有append提示的串行insert。你也可以发出ALTER TABLEMOVE COMPRESS,从而将表里已有的数据进行压缩。

 

对于压缩表的update操作的效率极其低下,如果一个表要经常被update,则不应该对该表启用压缩。因为update时,需要先将数据解压,然后对解压后的数据进行更新,更新结束以后再次压缩。

 

除了LOB以外的数据类型都可以被压缩。另外,如果表里的列超过255个,则压缩被禁用。也提供对索引的压缩,包括B树索引和bitmap索引,以及IOT表。

 

来了解一下压缩表的内部结构。

SQL> create table t_seed as

  2  select chr(trunc(dbms_random.value(65,67))) c1,chr(trunc(dbms_random.value(65,67))) c2,

  3  trunc(dbms_random.value(0,2)) c1flag,trunc(dbms_random.value(0,2)) c2flag

  4  from dba_objects where rownum<=1000;

 

表已创建。

 

SQL> create table t_comp_1 compress as

  2  select decode(c1flag,1,lpad(c1,10,c1),null) as c1,

  3  decode(c2flag,1,lpad(c2,10,c2),null) as c2 from t_seed;

 

表已创建。

 

SQL> select distinct dbms_rowid.rowid_relative_fno(rowid) as file#,

  2  dbms_rowid.rowid_block_number(rowid) as block# from t_comp_1 order by file#,block#;

 

     FILE#     BLOCK#

---------- ----------

         4       7604

         4       7605

 

SQL> alter system dump datafile 4 block 7604;

 

系统已更改。

 

data_block_dump,data header at 0xcc19a7c

===============

tsiz: 0x1f80

hsiz: 0x5ce

pbl: 0x0cc19a7c

bdba: 0x01001db4

     76543210

flag=-0------

ntab=2      这里的2说明该数据块里有2个表,一个symbol table,一个实际的表。

nrow=725

frre=-1

fsbo=0x5ce

fseo=0x1147

avsp=0x3d

tosp=0x3d

    r0_9ir2=0x0

    mec_kdbh9ir2=0x9     这里的9说明该数据块里重复值有9种情况。

                  76543210

    shcf_kdbh9ir2=----------

              76543210

    flag_9ir2=--R----C

        fcls_9ir2[3]={ 0 32768 32768 }

0x1c:pti[0] nrow=11 offs=0    这部分说明symbol table里有6条记录。

0x20:pti[1] nrow=717    offs=11   这部分说明该数据块里存放的实际表里的记录行数为717行。

0x24:pri[0] offs=0x1f61

0x26:pri[1] offs=0x1f5c

......................

tab 0, row 0, @0x1f61

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: *NULL*

bindmp: 00 a7 02 ff ff

tab 0, row 1, @0x1f5c

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 50 02 ff 0a

tab 0, row 2, @0x1f57

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 6c 02 ff 09

tab 0, row 3, @0x1f52

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: *NULL*

bindmp: 00 55 02 0a ff

tab 0, row 4, @0x1f4d

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 34 02 0a 0a

tab 0, row 5, @0x1f48

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 22 02 0a 09

tab 0, row 6, @0x1f43

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: *NULL*

bindmp: 00 5b 02 09 ff

tab 0, row 7, @0x1f3e

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 2c 02 09 0a

tab 0, row 8, @0x1f39

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  41 41 41 41 41 41 41 41 41 41

col  1: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 38 02 09 09

tab 0, row 9, @0x1f66

tl: 13 fb: --H-FL-- lb: 0x0  cc: 1

col  0: [10]  41 41 41 41 41 41 41 41 41 41

bindmp: 00 06 d2 41 41 41 41 41 41 41 41 41 41

tab 0, row 10, @0x1f73

tl: 13 fb: --H-FL-- lb: 0x0  cc: 1

col  0: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 06 d2 42 42 42 42 42 42 42 42 42 42

 

以上tab 0的部分就是所谓的symbol table里所存放的记录了。可以看到一共有11条记录。实际就是对c1c2这两列所存在的值的穷举,如下所示:

可能的情况

C1

C2

1

AAAAAAAAAA

Null

2

BBBBBBBBBB

Null

3

Null

AAAAAAAAAA

4

Null

BBBBBBBBBB

5

AAAAAAAAAA

AAAAAAAAAA

6

AAAAAAAAAA

BBBBBBBBBB

7

BBBBBBBBBB

AAAAAAAAAA

8

BBBBBBBBBB

BBBBBBBBBB

9

Null

Null

 

而在该数据块里的每条记录就是对上面这9种情况的描述,不过它穷举出来的情况要更多点。

多出来的也就是最后两条cc1记录。也就是说有可能只压缩一列。在我们这个例子里,因为只有两个列,所以没有这种情况。如果表里的列很多,则有可能只压缩一列。

tab 0里的每条记录里,记录了c1列和c2列的可能的取值,同时还记录了bindmp,这表示该种组合的一些元数据。比如对于symbol table里的row 4来说:

tab 0, row 4, @0x1f4d

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: [10]  42 42 42 42 42 42 42 42 42 42

bindmp: 00 34 02 0a 0a

前两个字节表示该数据块里有多少条记录属于该组合。这里为十六进制的00 34,换算成十进制,也就是52。说明在该数据块里的717条记录当中,有52条记录的(c1,c2)=(BBBBBBBBBB, BBBBBBBBBB)02表示压缩的列的个数为2个列。

我们来验证一下:

SQL> select count(*) from(

  2  select c1,c2 from t_comp_1 where rownum<=717)

  3  where c1='BBBBBBBBBB' and c2='BBBBBBBBBB';

 

  COUNT(*)

----------

        52

 

tab 0以后的部分就是tab 1,这部分就是数据块里实际存放的记录了。

tab 1, row 0, @0x1f34

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: [10]  42 42 42 42 42 42 42 42 42 42

col  1: *NULL*

bindmp: 2c 00 01 02 03

tab 1, row 1, @0x1f2f

tl: 5 fb: --H-FL-- lb: 0x0  cc: 2

col  0: *NULL*

col  1: *NULL*

bindmp: 2c 00 01 02 00

..................

转储的时候,进行了解压。把c1c2的值都显示出来。实际物理存放的值是:

bindmp: 2c 00 01 02 03

..................

也就是对每条记录来说,只占用了5个字节。其中,2c 00应该表示symbol table的物理地址。02表示压缩了2个字段。03表示在symbol table里的记录行号。这里的03也就是symbol table里的row 3所记录的c1c2的值。换句话说,该表里的第一条记录的(c1,c2)也就等于('BBBBBBBBBB', Null)

 

这时再创建一个压缩表,该表里多加两个列:

SQL> create table t_comp_2 compress as

  2  select decode(c1flag,1,lpad(c1,10,c1),null) as c1,rownum id1,

  3  decode(c2flag,1,lpad(c2,10,c2),null) as c2,

  4  chr(trunc(dbms_random.value(65,80))) c3 from t_seed;

 

表已创建。

 

SQL> select distinct dbms_rowid.rowid_relative_fno(rowid) as file#,

  2  dbms_rowid.rowid_block_number(rowid) as block# from t_comp_2 order by file#,block#;

 

     FILE#     BLOCK#

---------- ----------

         4       6900

         4       6901

 

SQL> alter system dump datafile 4 block 6900;

 

系统已更改。

 

打开转储文件,可以看到下面的部分:

    r0_9ir2=0x0

    mec_kdbh9ir2=0x18

                  76543210

    shcf_kdbh9ir2=----------

              76543210

    flag_9ir2=--R---OC

        fcls_9ir2[4]={ 0 32768 32768 32768 }

        perm_9ir2[4]={ 0 3 1 2 }  多出了这一行。

这一行非常重要,因为在压缩表中,oracle会修改列的排列顺序。非压缩表里,列在物理存放的时候,按照数据字典里记录的顺序,也就是col1col2col3....。比如:

SQL> desc t_comp_2

 名称                                      是否为空? 类型

 ----------------------------------------- -------- ----------------------------

 C1                                                 VARCHAR2(2)

 ID1                                                NUMBER

 C2                                                 VARCHAR2(2)

 C3                                                 VARCHAR2(2)

也就是说,正常表来说,在数据块里存放列时,会这样放:

col 0 XXXXXXX   ->表示放的是C1列的值

col 1 XXXXXXX   ->表示放的是ID1列的值

col 2 XXXXXXX   ->表示放的是C2列的值

col 3 XXXXXXX   ->表示放的是C3列的值

 

但是,在压缩表里,col 0已经不表示c1列的值了,col 1也不是ID1列的值。怎么对应呢,就是从这里的perm_9ir2[4]={ 0 3 1 2 }来对应。这里的0312表示物理存储时的列的下标。也就是说col 0对应数据字典里的col 1,在这里也就是C1列;col 3对应ID1col 1对应C2col 2对应C3

 

找到tab 1部分,比如:

tab 1, row 0, @0x1ea9

tl: 9 fb: --H-FL-- lb: 0x0  cc: 4

col  0: [10]  42 42 42 42 42 42 42 42 42 42  ->表示放的是C1列的值

col  1: *NULL*                               ->表示放的是C2列的值

col  2: [ 1]  49                             ->表示放的是C3列的值

col  3: [ 2]  c1 02                          ->表示放的是ID1列的值

bindmp: 2c 00 03 03 12 28 ca c1 02           ->这里2c 00表示symbol table的地址。而03

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9842/viewspace-344546/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/9842/viewspace-344546/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值