在实验手册(一)中介绍了IM的基础操作,objects如何开启IM column store。
链接:http://blog.csdn.net/badly9/article/details/49746825
虽然在之前的操作中我们对objects开启了IM column store,但是这些objects仍然没有被加载到IM column store中。默认情况下,Oracle会自主决定什么时候将表加载如IM中。或者我们可以通过执行DBMS_INMEMORY包来手动加载objects到IM中。
通过设置sub-clause PRIORITY可以自动加载objects到IM中。
默认情况下,Oracle在第一次访问objects之后加载该objects到IM中(比如全表扫描)。如果一个object开启了IM并且被查询之后,而且如果查询需要的数据并没有被加载到IM,数据库会通过后台进程异步的从buffer cache和disk中加载需要的数据到IM中,在数据逐步加载到IM中之后,之后的查询就会可以使用IM。
IM通过后台一组worker processes加载数据。每个worker process负责加载该objects的一部分。加载使用的是流机制,同时负责将数据的分列和压缩。在这个过程中,数据库正常运行。
就如一个表空间在硬盘上被分为多个extent,IM也被分成多个In-Memory Compression Units(IMCUs)。每个worker process会被分配自己的IMCU,加载objects的子集到IMCU中。数据在被加载到IMCU的过程中不会进行任何的分类和排序。加载数据的顺序和数据行实际存储的顺序相同。IMCU的大小和数量由系统内部算法决定。
通过视图V$IM_SEGMENTS和V$IM_USER_SEGMENTS能够查看objects是否被加载到IM中以及加载状态。如果segment仅仅设置了IM相关属性而没有被加载,则在这两个视图中并不会有与该segment对应的数据。
实验过程如下:
1.将sales表开启IM
SQL> ALTER TABLE SALES INMEMORY;
Table altered.
2.查询数据字典来确认表是否开启IM
SQL> SELECT INMEMORY, INMEMORY_PRIORITY, INMEMORY_COMPRESSION,
2 INMEMORY_DISTRIBUTE, INMEMORY_DUPLICATE
3 FROM USER_TABLES
4 WHERE TABLE_NAME = 'SALES';
INMEMORY INMEMORY INMEMORY_COMPRESS INMEMORY_DISTRI INMEMORY_DUPL
-------- -------- ----------------- --------------- -------------
ENABLED NONE FOR QUERY LOW AUTO NO DUPLICATE
3.查看V$IM_SEGMENTS的结构
SQL> DESC V$IM_SEGMENTS
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER VARCHAR2(128)
SEGMENT_NAME VARCHAR2(128)
PARTITION_NAME VARCHAR2(128)
SEGMENT_TYPE VARCHAR2(18)
TABLESPACE_NAME VARCHAR2(30)
INMEMORY_SIZE NUMBER
BYTES NUMBER
BYTES_NOT_POPULATED NUMBER
POPULATE_STATUS VARCHAR2(9)
INMEMORY_PRIORITY VARCHAR2(8)
INMEMORY_DISTRIBUTE VARCHAR2(15)
INMEMORY_DUPLICATE VARCHAR2(13)
INMEMORY_COMPRESSION VARCHAR2(17)
CON_ID NUMBER
4.查询V$IM_SEGMENTS中的数据
SQL> SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, BYTES_NOT_POPULATED
2 FROM V$IM_SEGMENTS;
no rows selected
可以看到虽然表sales设置了IM相关属性,但是由于没有加载,在V$IM_SEGMENTS中找不到与之对应的数据。
5.通过全表扫描读取sales表,由于表上有index,在count操作中可能会用到,所以使用了hint通过全表扫描读取数据。
SQL> SELECT /*+ FULL (s) */ COUNT(*) FROM SALES s;
COUNT(*)
----------
918843
6.在执行上边的语句的时候可以看到ora_w***_orcl进程占用更多资源,这个进程就是用于加载数据到IM的后台进程。
7.查询V$IM_SEGMENTS中的数据
SQL> col owner for a10
SQL> col SEGMENT_NAME for a10
SQL> SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, BYTES_NOT_POPULATED
2 FROM V$IM_SEGMENTS;
OWNER SEGMENT_NA POPULATE_ BYTES_NOT_POPULATED
---------- ---------- --------- -------------------
BADLY9 SALES COMPLETED 0
可以看到对SALES表的加载已经完成,还有0 bytes的数据没有加载。
8.我们可以通过V$IM_SEGMENTS视图查看该objects在IM中真正存储的大小,该视图中bytes列代表的是该表存储在磁盘上占的空间大小,INMEMORY_SIZE代表存储到IM中之后的大小。
SQL> SELECT OWNER, SEGMENT_NAME, BYTES, INMEMORY_SIZE
2 FROM V$IM_SEGMENTS;
OWNER SEGMENT_NA BYTES INMEMORY_SIZE
---------- ---------- ---------- -------------
BADLY9 SALES 33554432 7536640
可以看到SALES表在IM中经过QUERY LOW压缩后只占到在硬盘中存储的22%左右,压缩比例还是很可观的。
9.清理实验环境,将SALES置为NO INMEMORY。
SQL> ALTER TABLE SALES NO INMEMORY;
Table altered.
10.查询V$IM_SEGMENTS进行确认
SQL> SELECT OWNER, SEGMENT_NAME, BYTES, INMEMORY_SIZE
2 FROM V$IM_SEGMENTS;
no rows selected
DBMS_INMEMORY包提供了两个PROCEDURE来手工加载数据到IM中:
Ø POPULATE:强制加载给定的表
Ø REPOPULATE:强制重新加载给定的表,在该表已经至少加载过一次之后才可使用。
实验过程如下:
1.将sales表开启IM
SQL> ALTER TABLE SALES INMEMORY;
Table altered.
2.查询V$IM_SEGMENTS视图,确认sales表没有被加载到IM中
SQL> SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, BYTES_NOT_POPULATED
2 FROM V$IM_SEGMENTS;
no rows selected
3.手工执行DBMS_INMEMORY.POPULATE procedure来加载sales表到IM中
SQL> EXEC DBMS_INMEMORY.POPULATE('BADLY9','SALES');
PL/SQL procedure successfully completed.
4.查询V$IM_SEGMENTS视图,确认sales表已经加载到IM中
SQL> SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, BYTES_NOT_POPULATED
2 FROM V$IM_SEGMENTS;
OWNER SEGMENT_NA POPULATE_ BYTES_NOT_POPULATED
---------- ---------- --------- -------------------
BADLY9 SALES COMPLETED 0
5.通过关闭IM、重新开启IM来清除存储sales表数据的IMCU
SQL> ALTER TABLE SALES NO INMEMORY;
Table altered.
SQL> ALTER TABLE SALES INMEMORY;
Table altered.
6.通过DBMS_INMEMORY.REPOPULATE prcedure及FORCE=>TRUE选项来加载SALES表到IM中。FORCE=>TRUE选项强制进行一次完整重新载入,类似于full refresh。
SQL> EXEC DBMS_INMEMORY.REPOPULATE('BADLY9','SALES', FORCE=>TRUE);
PL/SQL procedure successfully completed.
7.查询V$IM_SEGMENTS视图,确认sales表已经加载到IM中
SQL> SELECT OWNER, SEGMENT_NAME, POPULATE_STATUS, BYTES_NOT_POPULATED
2 FROM V$IM_SEGMENTS;
OWNER SEGMENT_NA POPULATE_ BYTES_NOT_POPULATED
---------- ---------- --------- -------------------
BADLY9 SALES COMPLETED 0
8.关闭sales表的IM设置
SQL> ALTER TABLE SALES NO INMEMORY;
Table altered.
通过设置PRIORITY clause可以设置objects在数据库open之后,object被alter之后,或者objects导入数据后自动加载objects到IM中。PRIORITY clause有5个级别,分别是:NONE、LOW、MEDIUM、HIGH、CRITICAL。只有在高级别的objects被加载到IM之后,比它级别低的objects才会开始加载到IM。(当然,如果一个objects被扫描则会立即加载到IM中,不受PRIORITY clause影响)
PRIORITY clause的默认值为none,代表着如果object没有被扫描则不会被主动加载到IM中。
加载算法在单实例和RAC环境中会有所不同,在下面的文章会介绍。
由于IM中的数据只存储在内存中而不会存储到disk中。所以每次数据库实例重启之后,整个IM的数据都要从disk中重新加载并进行列化处理。
实验过程如下:
1.将测试表SALES的PRIORITY级别改为HIGH。
SQL> ALTER TABLE SALES INMEMORY PRIORITY HIGH;
Table altered.
2.将测试表CUSTOMERS的PRIORITY级别改为MEDIUM。
SQL> ALTER TABLE CUSTOMERS INMEMORY PRIORITY MEDIUM;
Table altered.
3.将测试表COST的PRIORITY级别改为LOW
SQL> ALTER TABLE COSTS INMEMORY PRIORITY LOW;
Table altered.
4.使用下面的语句查看这三张表的加载顺序:
COL OBJECT_NAME FORMAT A15
SELECT a.OBJECT_NAME, b.INMEMORY_PRIORITY, b.POPULATE_STATUS,
TO_CHAR(c.CREATETIME, 'MM/DD/YYYY HH24:MI:SS.FF2') START_POP,
TO_CHAR(MAX(d.TIMESTAMP),'MM/DD/YYYY HH24:MI:SS.FF2') FINISH_POP
FROM DBA_OBJECTS a, V$IM_SEGMENTS b,
V$IM_SEGMENTS_DETAIL c, V$IM_HEADER d
WHERE OBJECT_NAME IN ('CUSTOMERS','SALES','COSTS')
AND a.OBJECT_NAME = b.SEGMENT_NAME
AND a.OBJECT_TYPE = 'TABLE'
AND a.OBJECT_ID = c.BASEOBJ
AND c.DATAOBJ = d.OBJD
GROUP BY a.OBJECT_NAME, b.INMEMORY_PRIORITY, b.POPULATE_STATUS,
c.CREATETIME
ORDER BY FINISH_POP;
OBJECT_NAME INMEMORY POPULATE_ START_POP FINISH_POP
--------------- -------- --------- ---------------------- ----------------------
SALES HIGH COMPLETED 11/10/2015 00:14:29.97 11/10/2015 00:14:31.43
CUSTOMERS MEDIUM COMPLETED 11/10/2015 00:16:27.67 11/10/2015 00:16:28.01
COSTS LOW COMPLETED 11/10/2015 00:16:28.59 11/10/2015 00:16:28.72
可以看到SALES、CUSTOMERS、COSTS三张表的加载顺序为:SALES=>CUSTOMERS=>COSTS。
5.现在我们来重启一下数据库来看一下加载顺序:
conn / as sysdba
shutdown immediate
startup
COL OBJECT_NAME FORMAT A15
SELECT a.OBJECT_NAME, b.INMEMORY_PRIORITY, b.POPULATE_STATUS,
TO_CHAR(c.CREATETIME, 'MM/DD/YYYY HH24:MI:SS.FF2') START_POP,
TO_CHAR(MAX(d.TIMESTAMP),'MM/DD/YYYY HH24:MI:SS.FF2') FINISH_POP
FROM DBA_OBJECTS a, V$IM_SEGMENTS b,
V$IM_SEGMENTS_DETAIL c, V$IM_HEADER d
WHERE OBJECT_NAME IN ('CUSTOMERS','SALES','COSTS')
AND a.OBJECT_NAME = b.SEGMENT_NAME
AND a.OBJECT_TYPE = 'TABLE'
AND a.OBJECT_ID = c.BASEOBJ
AND c.DATAOBJ = d.OBJD
GROUP BY a.OBJECT_NAME, b.INMEMORY_PRIORITY, b.POPULATE_STATUS,
c.CREATETIME
ORDER BY FINISH_POP;
OBJECT_NAME INMEMORY POPULATE_ START_POP FINISH_POP
--------------- -------- --------- ---------------------- ----------------------
SALES HIGH COMPLETED 11/10/2015 00:24:23.59 11/10/2015 00:24:24.85
CUSTOMERS MEDIUM COMPLETED 11/10/2015 00:24:24.86 11/10/2015 00:24:25.25
COSTS LOW COMPLETED 11/10/2015 00:24:25.26 11/10/2015 00:24:25.41
结果和我们之前的验证相同,都是先加载完高级别的objects才会开始加载低级别的objects。
DUPLICATE clause:
在RAC环境中,每个节点拥有自己的IM Area。一个objects根据DUPLICATE clause的设置将一样的数据加载到多个IM Area中。
默认是NO DUPLICATE设置,表示在数据库的IM中对一个objects在所有节点中合起来只保存一份。举例说明,比如三节点的RAC中,对于分区表SALES来讲可能2012年份的数据在1节点,2013年份的数据在2节点,2014年份的数据在3节点,每个分区只保存在一个节点上。
为了提升可用性,也可以设置为DUPLICAET ALL,在每个节点上都保存一份。举例说明,还是刚才那个SALES表的请款下,1,2,3三个节点各保存一份完整sales表数据到各自的IM中。在任意一个节点上都可以获取查询需要的数据。
在设置为DUPLICATE ALL的情况下
DISTRIBUTE clause:
如果一个objects因为太大无法被加载到一个IM Area中,还可以通过DISTRIBUTE clause的设置将它分成几个数据片分别加载到不同的节点中。
默认情况下DISTRIBUTE clause的默认值为AUTO-DISTRIBUTE,这时候是否将objects分布式分布在不同的节点上由Oracle内部算法决定。这个参数对于单实例没有影响,在RAC环境中,默认存在IM中的表会分布在各个节点之中。