在Oracle 11g中,Advanced Compression是一个重要引入的新特性。Advanced Compression针对OLTP下的数据对象、Dataguard Redo Log等进行了较大程度的优化。在笔者之前的文章中,也针对此项特性进行了浅析。
目前,还有很多系统是运行在Oracle 10g乃至9i下,对这些系统而言,Oracle压缩有一些什么特性呢?本篇一起来探讨。
1、环境准备
我们选择Oracle 10gR2作为环境,同时准备原始数据表t_source,作为参照对象。
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
准备数据,观察在未使用10g压缩特性情况下,数据空间使用情况。
SQL> create table t_source as select * from dba_objects where wner='SCOTT' or wner='SYSMAN';
Table created
SQL> select count(*) from t_source;
COUNT(*)
----------
1367
SQL> exec dbms_stats.gather_table_stats(user,'T_SOURCE',cascade => true);
PL/SQL procedure successfully completed
--段级别空间使用分析
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SOURCE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 19 196608 24 3
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SOURCE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 23 DISABLED
在利用所有行rowid情况,分析每个数据块的平均容纳行数。
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_source group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 20 46
4 21 76
4 22 76
4 23 74
4 24 78
4 4105 73
4 4106 71
4 4107 71
4 4108 73
4 4109 77
4 4110 76
4 4111 74
4 4112 73
4 4114 77
4 4115 74
4 4116 75
4 4117 45
4 4119 81
4 4120 77
19 rows selected
SQL> select EXTENT_ID, FILE_ID, BLOCK_ID, BYTES, BLOCKS from dba_extents where wner='SCOTT' and segment_name='T_SOURCE';
EXTENT_ID FILE_ID BLOCK_ID BYTES BLOCKS
---------- ---------- ---------- ---------- ----------
0 4 17 65536 8 –第一个分区
1 4 4105 65536 8
2 4 4113 65536 8
从上面准备的数据表t_source的情况看,在没有使用压缩的情况下分配三个分区共24个数据块。1367条记录分布在19个数据块上,平均每个数据块容纳大约70-80行记录。
2、压缩表建立实验
下面建立压缩数据表。在建表的第一分区分配的时候,就采用压缩方式进行。
SQL> create table t_compress compress as select * from t_source;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T_COMPRESS',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_COMPRESS';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4123 131072 16 2
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_COMPRESS';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 11 ENABLED
我们当创建数据表就指定compress选项,数据表创建后就是压缩属性的。相同的数据量,只分配了2个分区。下面看具体行的使用情况。
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_compress group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4124 178
4 4125 199
4 4126 177
4 4127 197
4 4128 189
4 4129 185
4 4130 207
4 4131 35
8 rows selected
具体行在块的分配上,只用到了8个数据块进行存储。平均每个数据块容纳数据行接近200行。
说明:当我们在创建数据表的时候,就指定了compress选项。那么直接进行CATS插入的时候,压缩功能开启。同时,压缩效果较优。
注意:我们这里面使用的是CATS方式插入数据,而不是日常OLTP方式。两种方式差异显著。
3、变化分配数据插入实验
CATS方式是一种“压实”的插入方式。如果我们在变化过程中改变了数据表的压缩选项,空间分配情况会如何呢?
我们首先准备一个数据表,非压缩状态下插入过一部分的数据。
--插入一部分数据;
SQL> create table t_sample as select * from t_source where mod(object_id,2)=0;
Table created
SQL> select count(*) from t_sample;
COUNT(*)
----------
695
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4139 131072 16 2
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
695 13 DISABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4140 80
4 4141 75
4 4142 74
4 4143 71
4 4144 74
4 4145 74
4 4146 75
4 4147 74
4 4148 79
4 4149 19
10 rows selected
分配情况和t_source的趋势相似。我们修改数据表属性。
SQL> alter table t_sample compress;
Table altered
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4139 131072 16 2
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
695 13 ENABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4140 80
4 4141 75
4 4142 74
4 4143 71
4 4144 74
4 4145 74
4 4146 75
4 4147 74
4 4148 79
4 4149 19
10 rows selected
注意:此时我们修改了数据表属性,变化为compress。但是,现有数据并没有被压缩,而是保留为原有的情况。平均块容量为80左右个数据行,而非压缩下的上百行。
笔者此时猜想了一种可能,有没有在内部已经进行了空间重排,形成行链接模式。经过测试,发现没有行链接。而且在修改数据表compress属性的时候,执行时间也没有进行复杂变化的机会。
那么,我们接下来插入剩下的数据,使用OLTP方式。
SQL> insert into t_sample select * from t_source where mod(object_id,2)=1;
672 rows inserted
SQL> commit;
Commit complete
SQL> select count(*) from t_sample;
COUNT(*)
----------
1367
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4139 196608 24 3
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 23 ENABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4140 80
4 4141 75
4 4142 74
4 4143 71
4 4144 74
4 4145 74
4 4146 75
4 4147 74
4 4148 79
4 4149 19
4 4150 74
4 4151 78
4 4152 75
4 4162 74
4 4163 75
4 4164 77
4 4165 69
4 4167 72
4 4168 78
19 rows selected
注意:此时虽然是压缩模式,但是使用OLTP insert方式后,数据表空间并没有节省!新分配空间,依然是使用非压缩方式——这点可以从块行容量上看出来。
解决方法——Move操作
解决的方法,我们可以采用move移动。Move是一种对数据表物理存储进行重排的过程,相当于在另一个磁盘上进行数据表重新组织,对应的空间乃至段头都要发生变化。Move是我们经常使用的降低HWM的方法。
SQL> alter table t_sample move;
Table altered
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4171 131072 16 2
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 11 ENABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4172 173
4 4173 173
4 4174 170
4 4175 179
4 4176 173
4 4177 166
4 4178 182
4 4179 151
8 rows selected
结论,我们发现空间被压缩,块行容量增加。
上面的实验,告诉我们几个结论:
ü Compress是数据段的一个属性。当我们单纯进行compress和nocompress切换的时候,数据已经分配空间是不会发生变化的;
ü 即使在compress的数据表,当我们进行常规OLTP数据插入的时候,是不会应用压缩选项的;
ü Move操作通过重新促使系统进行数据空间分配的配比,可以应用实现compress的结果——注意是结果,数据表被压缩!
下面我们通过几个极端情况实验,继续分析10gR2的Compression。
下面我们通过几个极端情况实验,继续分析10gR2的Compression。
3、极端情况实验(1)
那么,上面我们验证数据compress选项切换无效,是不是和我们已经分配过的记录有关系呢?我们首先建立一张空表。
SQL> create table t_sample1 as select * from t_source where 1=0;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE1',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE1';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4139 65536 8 1
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
0 0 DISABLED
我们创建空表,没有记录,但是默认有一个分区分配过来。
SQL> alter table t_sample1 compress;
Table altered
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
0 0 ENABLED
SQL> insert into t_sample1 select * from t_source;
1367 rows inserted
SQL> commit;
Commit complete
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE1',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE1';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4139 196608 24 3
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 20 ENABLED
依然是使用三个分区,依然是没有进行压缩。
SQL> alter table t_sample1 move;
Table altered
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE1',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE1';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4187 131072 16 2
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
1367 11 ENABLED
结论:即使没有一行数据插入的情况下,我们使用compress的数据表,利用OLTP方式插入,也不能实现压缩。依然需要手工的压缩过程。
4、极端情况实验(2)
对实验数据表t_sample1继续插入数据时,新分配的数据块是可能应用压缩格式的。
SQL> insert into t_sample1 select * from t_source;
1367 rows inserted
SQL> commit;
Commit complete
此时,数据行会出现部分压缩的现象。
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE1',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE1';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4187 262144 32 4
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
2734 31 ENABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample1 group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4138 83
4 4139 85
4 4140 83
4 4141 81
4 4142 84
4 4143 79
4 4144 79
4 4148 83
4 4149 83
4 4150 88
4 4151 88
4 4152 27
4 4188 193
4 4189 184
4 4190 184
4 4191 177
4 4192 188
4 4193 199
4 4194 199
4 4195 43
4 4196 84
4 4197 82
4 4198 86
4 4199 82
4 4200 90
25 rows selected
在我们持续增加数据的时候,会出现一定的压缩情况。
SQL> alter table t_sample1 move;
Table altered
SQL> exec dbms_stats.gather_table_stats(user,'T_SAMPLE1',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SAMPLE1';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 4211 196608 24 3
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SAMPLE1';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
2734 19 ENABLED
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, count(*) from t_sample1 group by dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) order by bno;
FNO BNO COUNT(*)
---------- ---------- ----------
4 4212 193
4 4213 184
4 4214 184
4 4215 177
4 4216 188
4 4225 199
4 4226 199
4 4227 191
4 4228 191
4 4229 178
4 4230 198
4 4231 174
4 4232 185
4 4490 199
4 4491 94
15 rows selected
通过move过程,实现完全压缩。
5、实现压缩的insert方式
从上面一系列实验中,我们可以看出Oracle 10g Compression对OLTP方式插入数据压缩效果较差。但是,在进行Direct Insert方法的时候,Compression的效果是可以的。
SQL> create table t_source as select * from dba_objects where wner='SYS';
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T_SOURCE',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_SOURCE';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 5395 3145728 384 18
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_SOURCE';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
22946 327 DISABLED
新原数据表没有压缩,共占用384个块,18个extent分区。下面建立一个新的数据表t_compress,采用压缩配置。
--建立空数据表;
SQL> create table t_compress as select * from dba_objects where 1=0;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T_COMPRESS',cascade => true);
PL/SQL procedure successfully completed
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_COMPRESS';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
0 0 DISABLED
开启压缩选项。
SQL> alter table t_compress compress;
Table altered
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_COMPRESS';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
0 0 ENABLED
之后采用direct insert方式插入数据。
SQL> insert /*+append */into t_compress select * from t_source;
22946 rows inserted
SQL> commit;
Commit complete
SQL> exec dbms_stats.gather_table_stats(user,'T_COMPRESS',cascade => true);
PL/SQL procedure successfully completed
SQL> select NUM_ROWS, BLOCKS, Compression from dba_tables where wner='SCOTT' and table_name='T_COMPRESS';
NUM_ROWS BLOCKS COMPRESSION
---------- ---------- -----------
22946 139 ENABLED
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SCOTT' and segment_name='T_COMPRESS';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
4 6835 2097152 256 17
可见,在10R2中,压缩主要是针对那些稳定数据。如果操作是频繁的增加修改和删除的OLTP操作,压缩是不进行或者效果很差的。
6、结论
从上面的实验中,我们可以看出:Oracle 10R2中的压缩技术主要是针对稳定数据表而言的。如果数据表很大,而且不会频繁的进行增加修改和删除操作,我们推荐使用压缩功能。如果我们对一个压缩表进行OLTP方式操作,压缩效应是不明显的。只有在Direct Insert等特殊的操作中,压缩才能体现出来。
Oracle 11g带给我们的Advanced Compression新特性,其中的Compression For OLTP在一定程度上缓解了这个难题。
再次说明,我们的解决方案,要依据版本、效果和特性进行。