对于传统的数据库,数据是以行的方式保存在据库块中,这种保存方式对于OLTP应用非常适合,但是对于大数据量的OLAP应用就显得没有那么高效了,而且随着历史数据的积累,历史数据会占用大量的空间,这些数据几乎不可能会被改动,但是却仍然会被一些DSS或者OLAP应用所查询,而这些查询往往并不关心一行数据的所有列,而只是关心特定的一些列,所以,列式存储对于这种应用无疑要优于行式存储。
EHCC特性的出现就很好的解决了这种困难,一方面它会以列式保存数据;另一方面它会对数据进行压缩。下面的两张图形就很好的显示了传统的行式存储和EHCC的列式存储的区别。
行式:
列式:
可以看到,使用EHCC之后,数据是以列的方式来存放的。下面,对EHCC具体存放数据的方式进行介绍。当一个对象(例如:表或分区)启用了EHCC 选项之后,数据会被保存到称为CU(Compression Unit)的逻辑单位当中,每个CU由若干个数据块构成,其中CU的前端会包含CU的header(头)信息,之后会包含一个用于记录DML语句是否发生在了某一行的位图,其他的空间用于保存表当中以列方式存放,而且被压缩的数据。
所以,从数据存放的方式角度来看,EHCC这个新特性看起来很像是磁盘版本的in-memory 选项,是Oracle 为进一步的实现内存级别的列式存储(in-memory选项)在做准备。而从应用的角度来讲,EHCC非常适合DSS系统,因为这些系统的数据在被装载之后很少被修改的数据,而且并发量不高。而从存储的角度来讲,由于EHCC能够对数据进行压缩,可以很大程度上节省存储空间。
EHCC 支持以下的压缩方式。
Query HIGH :至少节省10倍空间,而且兼顾查询性能。
Query LOW :至少节省6倍空间,查询性能更好。
Archive HIGH :至少节省15倍空间,最大化节省空间。
Archive LOW :至少节省10被空间,查询性能要优于Archive HIGH。
另外,Archive HIGH和Archive LOW 压缩方式更适合于只读或者极少被更改的数据。
EHCC特性支持普通的数据表和分区,也就是说,用户可以使用下面的语句在表或者分区级别来指定EHCC属性。
创建一个使用了EHCC特性的表
SQL> create table sales_order_his compress for archive low;
SQL> insert /*+APPEND*/ into sales_order_his select * from sales_order;
创建一个分区表,但是只有指定的分区使用了EHCC
SQL> create table orders (cid, pid, sid, price, discount, odate)
partition by range (cid)
partition p1 values less than (100000) nocompress,
partition p2 values less than (200000) compress for archive low,
partition p3 values less than (300000) compress for query high,
partition p4 values less than (maxvalue) compress for query low)
enable row movement
as select * from prev_orders;
用户在决定使用哪一种压缩方式之前还可以通过调用下面的下面的pl/sql 存储过程来了解不同方式能够实现的压缩情况。
例如:
SQL> declare
v_blkcnt_cmp pls_integer;
v_blkcnt_uncmp pls_integer;
v_row_cmp pls_integer;
v_row_uncmp pls_integer;
v_cmp_ratio number;
v_comptype_str varchar2(60);
begin
dbms_compression.get_compression_ratio(
scratchtbsname => upper('&ScratchTBS'),
ownname => user,
tabname => upper('&TableName'),
partname => NULL,
comptype => dbms_compression.comp_for_query_high,
blkcnt_cmp => v_blkcnt_cmp,
blkcnt_uncmp => v_blkcnt_uncmp,
row_cmp => v_row_cmp,
row_uncmp => v_row_uncmp,
cmp_ratio => v_cmp_ratio,
comptype_str => v_comptype_str);
dbms_output.put_line('Estimated Compression Ratio: '||to_char(v_cmp_ratio));
dbms_output.put_line('Blocks used by compressed sample: '||to_char(v_blkcnt_cmp));
dbms_output.put_line('Blocks used by uncompressed sample: '||to_char(v_blkcnt_uncmp));
end;
对于使用了EHCC的表,只有在使用以下的方式向表当中加载数据时才会触发数据压缩。
直接路径insert语句
并行的DML语句
直接路径sqlloader
CTAS(create table as select)
注意:对于启用了EHCC的表,原有的数据不会被压缩,只有新增的数据才会被压缩。
而对于传统的dml语句,会通过更新CU头当中的位图并添加新行���方式来保证数据的一致性。
对于单行的insert 语句:新添加的行贿通过OLTP方式被压缩。
对于update语句:修改会被转换为delete 和insert。
对于delete 语句:删除只会在CU头当中对应的位上将该行标识成无效
表被压缩之后,当使用sql语句查询表当中的数据时,基本的过程如下图所示(下图展示了查询被offload 到cell 的情况)。
过程1:客户端发出了查询语句(select sum(sales) from sales_detail where date=24-Sep-14’)
过程2:这个查询被数据库接受之后发送给Exadata的存储网格(各个cell节点)。
过程3:每个cell节点offload 一部分工作负载,在本地上运行对应的查询语句
过程4:每个cell节点只把sales和date列得数据进行解压,并找到满足条件的数据进行汇总。
过程5:每个cell节点将自己汇总的结果发送给数据库节点,数据库节点再将最后的结果进行汇总并返回给数据。
可以看到:整个过程中只有需要的两个列被访问,而且工作负载被分配到了多个cell上完成。
最后,我们通过一个简单的测试来对比使用EHCC 和没有使用EHCC的差别。
1. 创建测试用表,并添加一些数据
SQL>CREATE TABLE test
( test_id NUMBER(10),
time_id DATE,
contents1 varchar2(1000)
) tablespace users;
SQL>begin
for i in 1..500000 loop
insert into test values (i, sysdate, dbms_random.string('L',1000));
end loop;
commit;
end;
SQL>create table test_ehcc compress for query high as select * from test;
我们对表test_ehcc使用了压缩。
2. 看一下这两张表所占用的空间。
SQL> select sum(bytes)/1024/1024, segment_name from dba_extents where owner='ALLEN' and segment_name='TEST'
group by segment_name;
SUM(BYTES)/1024/1024
--------------------
SEGMENT_NAME
--------------------------------------------------------------------------------
667
TEST
select sum(bytes)/1024/1024, segment_name from dba_extents where owner='ALLEN' and segment_name='TEST_EHCC'
group by segment_name;
SUM(BYTES)/1024/1024
--------------------
SEGMENT_NAME
--------------------------------------------------------------------------------
320
TEST_EHCC
可以看到经过了EHCC压缩之后的表所占用的空间要小很多。
3.刷新buffer cache,并分别对表test 和test_ehcc 执行下面的查询
SQL>alter system flush buffer_cache;
SQL>set timing on
SQL> select count(test_id) from test where time_id < sysdate;
COUNT(TEST_ID)
--------------
500000
Elapsed: 00:00:06.90 <<<<<<<<<<<
Execution Plan
----------------------------------------------------------
Plan hash value: 1950795681
--------------------------------------------------------------------------------
---
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
---
| 0 | SELECT STATEMENT | | 1 | 22 | 23036 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 22 | ||
|* 2 | TABLE ACCESS STORAGE FULL| TEST | 509K| 10M| 23036 (1)| 00:00:01 | <<<<<<<<<<
SQL> select count(test_id) from test_ehcc where time_id < sysdate;
COUNT(TEST_ID)
--------------
500000
Elapsed: 00:00:03.17 <<<<<<<
Plan hash value: 2868897916
--------------------------------------------------------------------------------
--------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
me |
--------------------------------------------------------------------------------
--------
| 0 | SELECT STATEMENT | | 1 | 22 | 10848 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 22 | | |
|* 2 | TABLE ACCESS STORAGE FULL| TEST_EHCC | 386K| 8304K| 10848 (1)| 00:00:01 | <<<<<
可以看到,在使用了ehcc 之后,同样的sql语句访问的数据更少,而且性能更好。
↧
Exadata 混合列压缩(EHCC)简介
↧