现在的位置: 首页 > 综合 > 正文

11GR2 Advanced Compress之OLTP Table Compress的研究

2014年10月25日 ⁄ 综合 ⁄ 共 15055字 ⁄ 字号 评论关闭

原文

http://space.itpub.net/15415488/viewspace-616769

11GR2 AdvancedCompress是非常值得购买的11G新特性。他引入了一种对于Oracle来说全新的压缩算法,更加合理和高效。

为什么说他值得购买?因为他需要的license不在Enterprise Edition License之中,需要额外掏钱。

    而他包含了若干个方面的压缩,其中又不乏我们所期望的特性。

    1.最常见的应该就是OLTPtable compression,支持了传统的DML语句。

    2.其次也会常常用到的是Backup
Data
compression,对Rmanbackup files、Datapump Files都有支持,能够给我们带来更节省的备份空间。更爽的是,compress是inline发生的,也就是说不需要我们认为的解压,比如说impdp就能自动识别expdp的压缩文件。

    3.第三我觉得也会应用到的是Network Traffic Compression。因为现在在我们生产数据库使用dataguard常常会出现delay几分钟的情况出现,有时是因为network
traffic bottleneck。但是如果使用Network Traffic Compression,redo在dataguard中传输也会被压缩和解压了,帮我们解决了这个头疼的问题。

    这篇文章,我做了一些针对OLTP table compress的实验和研究,主要从三方面:微观上、性能上和宏观上。

<从微观上分析>
分析之前,我们先了解下OLTP table compress的原理。

当我们对一个表使用alter table xxx compress for OLTP语句时,就跟传统compress一样,对已有的数据不受影响。

但是他对一个block中新insert或update的行最开始也不受影响,知道这个block达到PCTFREE时才触发compress。

于是,现在新的compress其实就是从老的方法:对每条新增的行做compress,变成了对一个block中的所有行批量处理。

这样就让大部分的insert/update语句没有性能上的损失,只对非常不幸的insert/update导致某个block刚好达到PCTFREE的语句有一些overhead。

当一个block由于触发压缩,而导致block的数据又低于PCTFREE的临界点后,这个block又可以接受更多的数据,会又一次达到PCTFREE,就会又一次压缩。

于是我从微观上的实验就是验证OLTP compress的这种算法的。

1.Before compression
SQL> create table haocompress
  2  (id1 char(200),id2 char(200),id3 char(200),id4 char(200))
  3  pctfree 20 pctused 50;

Table created.

我建立了一行800字节的表,他的pctfree是20%,我的block size是8k。也就是说从理论上当第一个block达到80%满的时候发生第一次压缩。也就是数据量达到8k*80%=6.4K左右。

也就是大约插入了8行的时候。首先插入2行。

insert into haocompress values('haozhu','zhuhao','1234','4321');
insert into haocompress values('haozhu','haozhu','4321','1234');

SQL> select EXTENT_ID,FILE_ID,BLOCK_ID,BLOCKS from dba_extents where SEGMENT_NAME='HAOCOMPRESS';

EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS
---------- ---------- ---------- ----------
         0          4        128          8
       
SQL> alter system dump datafile 4 block min 128 block max 131;  

System altered.

tab 0, row 0, @0x1c79
col  0: [200]
68 61 6f 7a 68 75
col  1: [200]
7a 68 75 68 61 6f
col  2: [200]
31 32 33 34
col  3: [200]
34 33 32 31

tab 0, row 1, @0x1952
col  0: [200]
68 61 6f 7a 68 75
col  1: [200]
68 61 6f 7a 68 75
col  2: [200]
34 33 32 31
col  3: [200]
31 32 33 34

以上是先观察下不压缩时的数据格式。

    
2.After enable compress for OLTP
SQL> alter table haocompress compress for OLTP;

Table altered.

SQL> select COMPRESSION,COMPRESS_FOR from dba_tables
  2  where table_name='HAOCOMPRESS';

COMPRESS COMPRESS_FOR
-------- ------------
ENABLED  OLTP

对这个表应用OLTP compress,接着来用我独门的show_space工具来看看:

SQL> exec hao_show_space('HAOCOMPRESS');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
MSSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.HAOCOMPRESS"
##################################################
UNUSED BLOCKS...........................6
UNUSED Bytes............................49152
------------------------HWM------------------------
Free Blocks.............................1
Used Blocks.............................1
##################################################
Total Blocks............................8
Total Bytes.............................65536
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................128
Last Used Block.........................2

SQL> alter system dump datafile 4 block min 128 block max 131;  

System altered.

从dump文件可以看见,对已有数据并没有压缩发生。

3.insert one more row

insert into haocompress values('haozhu','zhuhao','7890','4321');

第三行仍然没有没有压缩:
tab 0, row 2, @0x162b
col  0: [200]
68 61 6f 7a 68 75
col  1: [200]
7a 68 75 68 61 6f
col  2: [200]
37 38 39 30
col  3: [200]
34 33 32 31

4.insert more rows reaching PCTFREE

我的一行是800字节,blocksize是8192字节。
PCTFREE是20,那么就是8192*80%=6553.6字节满时触发压缩。
也就是插入第6553.6/800=8行时触发。

(4)insert into haocompress values('haozhu','zhuhao2','7890','4321');
(5)insert into haocompress values('haozhu','zhuhao3','7890','0000');
(6)insert into haocompress values('haozhu2','zhuhao','3333','1234');
(7)insert into haocompress values('haozhu3','zhuhao','3333','1234');
(8)insert into haocompress values('haozhu3','zhuhao8','444','8888');

tab 0, row 0, @0x1c6c
col  0: [200]
68 61 6f 7a 68 75  (haozhu)
bindmp: 00 06 fa 00 c8 68 61 6f 7a 68 75

tab 0, row 1, @0x1ed3
col  0: [200]
34 33 32 31        (4321)
bindmp: 00 04 fa 00 c8 34 33 32 31

tab 0, row 2, @0x1d39
col  0: [200]
7a 68 75 68 61 6f  (zhuhao)
bindmp: 00 04 fa 00 c8 7a 68 75 68 61 6f

tab 0, row 3, @0x1e06
col  0: [200]
31 32 33 34        (1234)
bindmp: 00 04 fa 00 c8 31 32 33 34

tab 0, row 4, @0x1b9f
col  0: [200]
37 38 39 30        (7890)
bindmp: 00 03 fa 00 c8 37 38 39 30

tab 0, row 5, @0x1ad2
col  0: [200]
33 33 33 33         (3333)
bindmp: 00 02 fa 00 c8 33 33 33 33

-------
tab 1, row 0, @0x1aca
bindmp: 2c 00 04 04 00 02 03 01
(haozhu,zhuhao,1234,4321)

tab 1, row 1, @0x1ac2
bindmp: 2c 00 04 04 00 00 01 03
(haozhu,haozhu,4321,1234)

tab 1, row 2, @0x1aba
bindmp: 2c 00 04 04 00 02 04 01
(haozhu,zhuhao,7890,4321)

tab 1, row 3, @0x19ea
bindmp: 2c 00 04 04 00 c9 7a 68 75 68 61 6f 32
(haozhu,zhuhao2,7890,4321)

tab 1, row 4, @0x1852
bindmp: 2c 00 04 04 00 c9 7a 68 75 68 61 6f 33
(haozhu,zhuhao3,7890,0000)

tab 1, row 5, @0x1782
bindmp: 2c 00 04 04 c9 68 61 6f 7a 68 75 32
(haozhu2,zhuhao,3333,1234)

tab 1, row 6, @0x16b2
bindmp: 2c 00 04 04 c9 68 61 6f 7a 68 75 33
(haozhu3,zhuhao,3333,1234)

tab 1, row 7, @0x1387
bindmp: 2c 02 03 02 ca 68 61 6f 7a 68 75 33
(haozhu3,zhuhao8,444,8888)

以上就是插入第八行后的block dump结果。

果然,当插入第八行的时候发生了压缩,这点得到了论证。

而且在这个block中,Oracle新增了一个表,就是红色部分,他就是所谓的Signal table,存放压缩表中相同的字段的片段。

现在观察蓝色部分,这才是我们正在的数据表,他仅仅存放了一个叫做bindmp的地址,而这个地址就指向了前面signal table的不同位置。具体这个地址如何解析,我没有深究。

在这里我要着重阐明几点:

1.signal table的每一行可以被compressed table的不同列引用。例如“haozhu”字样在以上的表中的第一列和第二列一共出现过6次,于是我们看:

tab 0, row 0, @0x1c6c
col  0: [200]
68 61 6f 7a 68 75  (haozhu)
bindmp: 0006fa 00 c8 68 61 6f 7a 68 75

原来,bindmp的第二个byte就是存放这个字样出现过多少次的。(也有可能第一个byte为00也是作为他的高位存在)。

2.signal table的排列顺序是按引用次数来排的。我们可以看出,引用次数从6到2递减排列。

3.signal table的某一个signal也可以作为前缀被引用。例如在compressed table中的“haozhu3”这样的一行,也其实引用了“haozhu”的signal(只是没有算在“haozhu”signal被引用的次数中)。

<从performance上分析>

看完了从微观上分析OLTP compress的新算法后,我们从update、insert、select等DML语句来看看他的性能情况。

是不是有很高的overhead?是不是真如Oracle自己号称的那样呢?

5.insert much more rows and compare with non-compress table

SQL> select count(*) from haocompress;

  COUNT(*)
----------
     16384

SQL> create table haonocompress as select * from haocompress;

Table created.

SQL>  exec hao_show_space('HAOCOMPRESS');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
MSSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.HAOCOMPRESS"
##################################################
UNUSED BLOCKS...........................38
UNUSED Bytes............................311296
------------------------HWM------------------------
Free Blocks.............................2
Used Blocks.............................600
##################################################
Total Blocks............................640
Total Bytes.............................5242880
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................640
Last Used Block.........................90

SQL> exec hao_show_space('HAONOCOMPRESS');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
MSSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.HAONOCOMPRESS"
##################################################
UNUSED BLOCKS...........................127
UNUSED Bytes............................1040384
------------------------HWM------------------------
Free Blocks.............................0
Used Blocks.............................2049
##################################################
Total Blocks............................2176
Total Bytes.............................17825792
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................2816
Last Used Block.........................1

6.insert performance

SQL> create table HAOCOMPRESS compress for oltp
  2  as
  3  select * from dba_objects;

Table created.

SQL> create table HAONOCOMPRESS
  2  as
  3  select * from dba_objects;

Table created.

SQL> set serveroutput on
SQL> exec hao_show_space('HAOCOMPRESS');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
MSSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.HAOCOMPRESS"
##################################################
UNUSED BLOCKS...........................1
UNUSED Bytes............................8192
------------------------HWM------------------------
Free Blocks.............................0
Used Blocks.............................63
##################################################
Total Blocks............................64
Total Bytes.............................524288
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................8248
Last Used Block.........................7

PL/SQL procedure successfully completed.

SQL> exec hao_show_space('HAONOCOMPRESS');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
MSSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.HAONOCOMPRESS"
##################################################
UNUSED BLOCKS...........................84
UNUSED Bytes............................688128
------------------------HWM------------------------
Free Blocks.............................0
Used Blocks.............................172
##################################################
Total Blocks............................256
Total Bytes.............................2097152
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................8448
Last Used Block.........................44

PL/SQL procedure successfully completed.

set timing on
set serveroutput on
exec runStats_pkg.rs_start;
insert into HAOCOMPRESS select * from HAOCOMPRESS;
commit;
exec runStats_pkg.rs_middle;
insert into HAONOCOMPRESS select * from HAONOCOMPRESS;
commit;
exec runStats_pkg.rs_stop(100);

Run1 ran in 75 hsecs
Run2 ran in 19 hsecs
run 1 ran in 394.74% of the time

Name                                  Run1        Run2        Diff
STAT...session logical reads         2,425       1,478        -947
STAT...HSC OLTP Space Saving       752,629           0    -752,629
STAT...CPU used by this sessio          65          39         -26
STAT...DB time                          85          45         -40
STAT...redo size                 4,554,932   1,410,592  -3,144,340

从insert的性能上来看,LIO增加了两倍,CPU增加了两倍,redo更是增加了2倍以上。

这些overhead应该是Block由于不断到达PCTFREE而引起不断的compress。

7.update performance

set timing on
set serveroutput on
exec runStats_pkg.rs_start;
update HAOCOMPRESS set object_name=to_char(rownum);
commit;
exec runStats_pkg.rs_middle;
update HAONOCOMPRESS set object_name=to_char(rownum);
commit;
exec runStats_pkg.rs_stop(100);

Run1 ran in 507 hsecs
Run2 ran in 247 hsecs
run 1 ran in 205.26% of the time

Name                                  Run1        Run2        Diff
STAT...session logical reads       122,627      57,341     -65,286
STAT...HSC OLTP Space Saving       687,461           0    -687,461
STAT...CPU used by this sessio         408         180        -228
STAT...DB time                         510         246        -264
STAT...redo size                42,392,660  24,231,484 -18,161,176

从update的性能上来看,compressed table用了蛮长的时间,LIO和CPU用了两倍以上。

Redo还是保持着2倍左右的overhead。

8.select performance
set timing on
set serveroutput on
set autotrace trace exp stat
exec runStats_pkg.rs_start;
select * from HAOCOMPRESS;
exec runStats_pkg.rs_middle;
select * from HAONOCOMPRESS;
exec runStats_pkg.rs_stop(1);

Run1 ran in 319 hsecs
Run2 ran in 338 hsecs
run 1 ran in 94.38% of the time

Name                                  Run1        Run2        Diff
STAT...session logical reads         4,025       4,570         545
STAT...CPU used by this sessio          69          62          -7
STAT...DB time                          86         101          15

从select查询的性能上看,查询的LIO要更少,这是显然的,因为少了很多的blocks。

CPU跟不compressed的table相比不相上下,这是非常不错的,这表明了对于以查询为主的表来说,在CPU overhead基本不变的情况下,会节省更多的IO。

<从宏观上分析>

宏观上主要看看OLTP compressed table和传统老的compressed table的空间利用情况的比较。
create table newcompress compress for oltp as select * from dba_objects;
create table oldcompress compress as select * from dba_objects;
create table notcompress  as select * from dba_objects;

SQL>  select table_name,COMPRESS_FOR from dba_Tables where table_name in ('NEWCOMPRESS','OLDCOMPRESS','NOTCOMPRESS');

TABLE_NAME                     COMPRESS_FOR
------------------------------ ------------
NOTCOMPRESS
OLDCOMPRESS                    BASIC
NEWCOMPRESS                    OLTP

                              
                              
SQL> select SEGMENT_NAME,sum(BYTES)/1024 KB from dba_extents
  2  where SEGMENT_NAME in ('NEWCOMPRESS','OLDCOMPRESS','NOTCOMPRESS')
  3  group by SEGMENT_NAME;                  
                              
SEGMENT_NAME                 KB                  
-------------------- ----------                  
OLDCOMPRESS                 512                  
NEWCOMPRESS                 512                  
NOTCOMPRESS                2048
  

两种压缩方式在最开始的时候差不多大。                
                                                             
SQL> update NEWCOMPRESS set object_name=to_char(rownum);                    

SQL> update OLDCOMPRESS set object_name=to_char(rownum);
SQL> update NOTCOMPRESS set object_name=to_char(rownum);

                                                                                                                          

SQL> select SEGMENT_NAME,sum(BYTES)/1024 KB from dba_extents            

  2  where SEGMENT_NAME in ('NEWCOMPRESS','OLDCOMPRESS','NOTCOMPRESS')  
  3  group by SEGMENT_NAME;                                             
                                                                        
SEGMENT_NAME                 KB                                         
-------------------- ----------                                         
OLDCOMPRESS                 768                                         
NEWCOMPRESS                 640                                         
NOTCOMPRESS                2048
                        

在大量更新后,我们发现,传统的compressed table逐渐丢失了自己compress的特性,因为新更新的行很有可能不在原来的block里,这样就导致不能引用原来block里的signal。这应该是传统compressed table的通病了吧。

而OLTP compressed table却依然保持良好的压缩性能。赞。

SQL> drop table OLDCOMPRESS;
SQL> drop table NEWCOMPRESS;
SQL> drop table NOTCOMPRESS;

create table newcompress compress for oltp as select * from dba_objects order by object_name;
create table oldcompress compress as select * from dba_objects order by object_name;
create table notcompress  as select * from dba_objects order by object_name;

SQL> select SEGMENT_NAME,sum(BYTES)/1024 KB from dba_extents         

  2  where SEGMENT_NAME in ('NEWCOMPRESS','OLDCOMPRESS','NOTCOMPRESS')
  3  group by SEGMENT_NAME;                                          

SEGMENT_NAME                 KB
-------------------- ----------
OLDCOMPRESS                 576
NEWCOMPRESS                 640
NOTCOMPRESS                2048

我们发现,如果将表里的数据order by一把,来的compressed table拥有更好的压缩比。

为什么呢?原来的compressed table可以对每一行新进来的时候做压缩。

而OLTP compressed table当他某个block达到PCTFREE时就需要压缩,这样本来排好序的数据就insert到下一个block中了。当第一个block压缩完毕迎接新数据的时候,迎接的新数据却变成了非常后面的数据了,无法引用当前block的signal。说简单一点,这应该是不停的block加入和退出freelist,导致本来排好序的数据插入了乱序的block中。(当然,这只是我的猜测,欢迎辩驳)

create index NEWCOMPRESSIDX on NEWCOMPRESS(object_name);
create index OLDCOMPRESSIDX on OLDCOMPRESS(object_name);
create index NOTCOMPRESSIDX on NOTCOMPRESS(object_name);

SQL> select SEGMENT_NAME,sum(BYTES)/1024 KB from dba_extents                      

  2  where SEGMENT_NAME in ('NEWCOMPRESSIDX','OLDCOMPRESSIDX','NOTCOMPRESSIDX')   

  3  group by SEGMENT_NAME;                                                       

SEGMENT_NAME                 KB
-------------------- ----------
NEWCOMPRESSIDX              512
NOTCOMPRESSIDX              512
OLDCOMPRESSIDX              512

最后,非常无聊的建立索引,得出的结论是,索引还是正常的,跟表的压缩与否没有直接联系。

抱歉!评论已关闭.