ITPub博客

首页 > 数据库 > Oracle > 12c in memory option学习笔记二_数据访问

12c in memory option学习笔记二_数据访问

原创 Oracle 作者:talio 时间:2014-09-17 09:26:15 0 删除 编辑

要了解In Memory区数据的访问方式,首先来看看In Memory区数据的组织结构。当enable某个表的inmemory属性后,该表会在首次访问或者数据库启动后加载到In Memory区(取决于priority属性的设置)。数据在IM区使用压缩列式存储,存放在每个IMCU中。在IMCU内部,Oracle为每个IMCU维护着一个对应的In Memory Storage Index, 用于记录IMCU单元列中的最大值,最小值等信息。对于每个IMCU,还会有相应的metadata dictionary信息,存储在metadata块中,也就是IM中的64K pool中,块的大小固定为64K。metadata dictionary记录对象信息,列信息等。

In Memory Storage Index的作用

In Memory Storage Index通过data pruning的机制来帮助提高查询性能:由于In Memory Storage Index中记录了该列的最小值,最大值信息,当查询语句的where条件中指定了某一列的值或范围时,则根据该索引的信息即可预判哪些IMCU需要继续访问,哪些可以直接跳过。

以下测试用来帮助理解In Memory Storage Index的使用:

SQL> create table test_im_access tablespace users as select rownum id,systimestamp time from dual connect by level <=10000000;
SQL> alter table test_im_access inmemory MEMCOMPRESS FOR QUERY LOW;
SQL> select count(*) from test_im_access; 
SQL> select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments where segment_name='TEST_IM_ACCESS';
SEGMENT_NAME                   INMEMORY_SIZE      BYTES POPULATE_
------------------------------ ------------- ---------- ---------
TEST_IM_ACCESS                     149028864  285212672 COMPLETED 

视图V_$IM_HEADER记录了每个IMCU的内存地址,分配大小等信息:

SQL> select IMCU_ADDR,ALLOCATED_LEN,USED_LEN from V_$IM_HEADER where OBJD=(select object_id from dba_objects where object_name='TEST_IM_ACCESS') order by IMCU_ADDR;
IMCU_ADDR        ALLOCATED_LEN   USED_LEN
---------------- ------------- ----------
00000003D81FFF88       7340032    1199852
00000003D88FFF88       7340032    6797528
00000003DBFFFFA0       7340032    6632157
00000003DC6FFFA0       8388608    7997296
00000003DCEFFFA0       8388608    7997296
00000003DD6FFFA0       8388608    7996030
00000003DDEFFFA0       8388608    7997304
00000003DE6FFFA0       8388608    7997296
00000003DEEFFFA0       8388608    7997376
00000003DF6FFFA0       8388608    7997320
00000003EC000000       8388608    8035475
00000003EC800000       8388608    7711008
00000003ED000000       8388608    7876120
00000003ED800000       8388608    7887481
00000003EE000000       8388608    7997296
00000003EE800000       8388608    7997304
00000003EF000000       8388608    7997352
00000003EF800000       8388608    7997304
18 rows selected.

视图V_$IM_COL_CU记录了每个IMCU的记录条数,最小值,最大值等信息。(将V_$IM_HEADER和V_$IM_COL_CU视图中的结果比对可发现似乎有一个IMCU分配了空间,但并没有载入数据,原因未知,测试了一些其他的压缩方式,没有重现该现象,这里没有深究。)

SQL> select HEAD_PIECE_ADDRESS IMCU_ADDR,COLUMN_NUMBER,DICTIONARY_ENTRIES,UTL_RAW.CAST_TO_NUMBER(MINIMUM_VALUE) MINIMUM_VALUE,UTL_RAW.CAST_TO_NUMBER(MAXIMUM_VALUE) MAXIMUM_VALUE
from V_$IM_COL_CU 
where OBJD=(select object_id from dba_objects where object_name='TEST_IM_ACCESS') and COLUMN_NUMBER=1 order by 1;
IMCU_ADDR        COLUMN_NUMBER DICTIONARY_ENTRIES  MINIMUM_VALUE  MAXIMUM_VALUE
---------------- ------------- ------------------ -------------- --------------
00000003D81FFF88             1             591600        4736151        5623550
00000003DBFFFFA0             1             495190        9468851       10000000
00000003DC6FFFA0             1             591600        8877251        9764650
00000003DCEFFFA0             1             591600        8285651        9173050
00000003DD6FFFA0             1             591500        7694151        8581450
00000003DDEFFFA0             1             591600        7102551        7989950
00000003DE6FFFA0             1             591600        6510951        7398350
00000003DEEFFFA0             1             591600        5919351        6806750
00000003DF6FFFA0             1             591600        5327751        6215150
00000003EC000000             1             642753              1         642753
00000003EC800000             1             596867         642754        1239620
00000003ED000000             1             582624        1239621        1822244
00000003ED800000             1             583466        1822245        2665550
00000003EE000000             1             591600        2369751        3257150
00000003EE800000             1             591600        2961351        3848750
00000003EF000000             1             591600        3552951        4440350
00000003EF800000             1             591600        4144551        5031950
17 rows selected. 

接下来,针对已载入IM区的test_im_access表作id=3的条件查询,并记录相关统计信息:

select name,value from v$mystat, v$statname 
where v$mystat.statistic# = v$statname.statistic# 
  and v$statname.name in 
  ('CPU used by this session',
   'IM scan rows',
   'IM scan rows valid',
   'IM scan CUs memcompress for query low',
   'IM scan CUs pruned'
  );
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
CPU used by this session                                                  2
IM scan CUs memcompress for query low                                     0
IM scan rows                                                              0
IM scan rows valid                                                        0
IM scan CUs pruned                                                        0
 
select * from test_im_access where id=3;
--上述语句的执行计划如下:
---------------------------------------------------------------------------------------------
| Id  | Operation                  | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                |   387 | 13545 |   438  (11)| 00:00:01 |
|*  1 |  TABLE ACCESS INMEMORY FULL| TEST_IM_ACCESS |   387 | 13545 |   438  (11)| 00:00:01 |
---------------------------------------------------------------------------------------------
--check stats again:
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
CPU used by this session                                                 12
IM scan CUs memcompress for query low                                    17
IM scan rows                                                       10000000
IM scan rows valid                                                   642753
IM scan CUs pruned                                                       16

统计结果如下:

Stat name

Value

CPU used by this session

10

IM scan CUs memcompress for query low

17

IM scan rows

10000000

IM scan rows valid

642753

IM scan CUs pruned

16

从统计结果可以看到,虽然test_im_access表采用’memcompress for query low’压缩列存储后占用的内存IMCU个数为17,但这里实际发生了完全扫描的IMCU个数只有一个(IM scan CUs memcompress for query low - IM scan CUs pruned),其中的16个块都发生了pruning。也就是只要访问这16个IMCU中的In Memory Storage Index, 即可确定我们要查询的记录不在这些IMCU中,从而跳过他们,而不用完全扫描整个IMCU。此外,该表有1千万条记录,由于IMCU data pruning的作用,我们实际只需要扫描其中一个IMCU的642753条记录。从该例也可以看出IM scan rows统计的并不是真正访问了的行数,它更像是一个估计的行数。

针对in-memory storage indexes,oracle提供了INMEMORY_PRUNING和NO_INMEMORY_PRUNING两个hint来控制这种索引的使用。继续上面的例子:

select /*+ NO_INMEMORY_PRUNING */* from test_im_access where id=3; 

Stat name

Value

CPU used by this session

40

IM scan CUs memcompress for query low

17

IM scan rows

10000000

IM scan rows valid

10000000

IM scan CUs pruned

0

可以看到,在使用NO_INMEMORY_PRUNING hint来禁用in-memory storage indexes的使用后,发生了pruning的CU个数为0,同样的查询需要访问IM中所有的17个IMCU,全部1千万条数据,CPU time也增加到了40。

实际上,Storage Index并不是12C In Memory Option中才有的新技术,了解过Exadata的应该知道这是在Exadata中就已经引入的技术了。Exadata的存储索引中除了标识最大值和最小值之外,还有一个标识用来表示在该存储单元中是否包含空值(null),从而使得寻找空值的查询效率更高。那么In Memory Storage Index是否也标识了空值呢?从已公开的资料我没有查询到这一点,还是用测试来验证一下:

insert into test_im_access select null,systimestamp time from dual connect by level <=10000;
commit;

等待片刻,待oracle将新的数据载入IM区后,执行以下语句并记录统计结果:

select count(*) from test_im_access where id is null; 

Stat name

Value

CPU used by this session

7

IM scan CUs memcompress for query low

17

IM scan rows

10010000

IM scan rows valid

1096690

IM scan CUs pruned

15

结果表明,在我们作空值查询时,In Memory Storage Index仍旧发挥了作用,其中的15个IMCU发生了data pruning,说明了In Memory Storage Index同样有对空值的标识。

关于data pruning, Oracle In Memory白皮书中还提到了通过metadata dictionary可以实现另一个级别的data pruning。但在测试中并没有观察到该技术的使用。

SIMD Vector Processing

从前面的测试统计信息来看,无论是否使用了data pruning技术,从IM区访问数据所占用的CPU time都是很低的。这是如何实现的呢?

Oracle在IM option中引入了SIMD(Single Instruction processing Multiple Data values)向量处理技术用于提高CPU效率,简单来说就是通过单条CPU指令作批量数据比对。Oracle给出了下图用于简单说明该技术的原理,即每次load一批数据到CPU上的SIMD寄存器(register)上,通过单条CPU指令来比较整批数据,将匹配的结果记录后,再接着比较下一批数据…

 In Memory Join操作

按Oracle的说法,IM区数据列式存储比较适用于用bloom filter方法来提升连接操作的效率。继续用测试来观察这一点:

创建测试表:

create table small_table tablespace users as select rownum id,systimestamp time from dual connect by level <=1000000; 
create table big_table (id number, TIME TIMESTAMP) tablespace users; 
 
Begin
  for i in 1..50 loop
    insert into big_table select rownum+1000000*(i-1) id,systimestamp time from dual connect by level <=1000000;
    commit;
  end loop;
end;
/ 
 
BEGIN
  dbms_stats.gather_table_stats(ownname =>'SYS',
    tabname => 'BIG_TABLE',
    degree=>8,
    method_opt => 'for all columns size auto',
    cascade => TRUE);
END;
/ 
 
BEGIN
  dbms_stats.gather_table_stats(ownname =>'SYS',
    tabname => 'SMALL_TABLE',
    degree=>8,
    method_opt => 'for all columns size auto',
    cascade => TRUE);
END;
/ 

将测试表载入IM区和keep buffer cache中,用于比较访问IM区和buffer cache时连接查询的不同:

alter table big_table inmemory;
alter table small_table inmemory;
alter table big_table storage (buffer_pool keep);
alter table small_table storage (buffer_pool keep);
select count(*) from big_table;
select count(*) from small_table;
 
SQL> select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments;
SEGMENT_NAME                             INMEMORY_SIZE      BYTES POPULATE_
---------------------------------------- ------------- ---------- ---------
BIG_TABLE                                    792788992 1342177280 COMPLETED
SMALL_TABLE                                   13828096   28311552 COMPLETED
--数据已加载到IM
 
SQL> select b.object_name,sum(NUM_BUF) from X$KCBOQH a, dba_objects b where a.OBJ#=b.object_id and b.object_name in ('BIG_TABLE','SMALL_TABLE') group by b.object_name;
OBJECT_NAME                    SUM(NUM_BUF)
------------------------------ ------------
SMALL_TABLE                            3322
BIG_TABLE                            158863
--数据已加载到cache

 

对测试表作连接查询,观察执行时间,执行计划和consistent gets的变化:

--这里取第二次执行时作观察,因为第一次执行会有一些硬解析的成本
SQL> set autotrace on
SQL> select count(*)
from big_table a,small_table b
where a.id=b.id and to_char(b.time,'yyyymmdd')='20140912'; 
  COUNT(*)
----------
   1000000
Elapsed: 00:00:09.35
 
---------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |     1 |    24 |  2632  (35)| 00:00:01 |
|   1 |  SORT AGGREGATE               |             |     1 |    24 |            |          |
|*  2 |   HASH JOIN                   |             | 10012 |   234K|  2632  (35)| 00:00:01 |
|   3 |    JOIN FILTER CREATE         | :BF0000     | 10000 |   175K|   114  (54)| 00:00:01 |
|*  4 |     TABLE ACCESS INMEMORY FULL| SMALL_TABLE | 10000 |   175K|   114  (54)| 00:00:01 |
|   5 |    JOIN FILTER USE            | :BF0000     |    50M|   286M|  2115  (21)| 00:00:01 |
|*  6 |     TABLE ACCESS INMEMORY FULL| BIG_TABLE   |    50M|   286M|  2115  (21)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("A"."ID"="B"."ID")
   4 - inmemory(TO_CHAR(INTERNAL_FUNCTION("B"."TIME"),'yyyymmdd')='20140912')
       filter(TO_CHAR(INTERNAL_FUNCTION("B"."TIME"),'yyyymmdd')='20140912')
   6 - inmemory(SYS_OP_BLOOM_FILTER(:BF0000,"A"."ID"))
       filter(SYS_OP_BLOOM_FILTER(:BF0000,"A"."ID"))
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         17  consistent gets
          0  physical reads
          0  redo size
        542  bytes sent via SQL*Net to client
        551  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

这里,测试语句的执行时间为9.35秒,consistent gets的个数为17,从执行计划可看到启用了bloom filter计算方法.

若是禁用bloom filter会有什么变化呢?

禁用bloom filter:
alter session set "_bloom_filter_enabled"=FALSE;
SQL> select count(*)
from big_table a,small_table b
where a.id=b.id and to_char(b.time,'yyyymmdd')='20140912';
  COUNT(*)
----------
   1000000
Elapsed: 00:00:45.18
--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |     1 |    24 |  2632  (35)| 00:00:01 |
|   1 |  SORT AGGREGATE              |             |     1 |    24 |            |          |
|*  2 |   HASH JOIN                  |             | 10012 |   234K|  2632  (35)| 00:00:01 |
|*  3 |    TABLE ACCESS INMEMORY FULL| SMALL_TABLE | 10000 |   175K|   114  (54)| 00:00:01 |
|   4 |    TABLE ACCESS INMEMORY FULL| BIG_TABLE   |    50M|   286M|  2115  (21)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("A"."ID"="B"."ID")
   3 - inmemory(TO_CHAR(INTERNAL_FUNCTION("B"."TIME"),'yyyymmdd')='20140912')
       filter(TO_CHAR(INTERNAL_FUNCTION("B"."TIME"),'yyyymmdd')='20140912')
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         17  consistent gets
          0  physical reads
          0  redo size
        542  bytes sent via SQL*Net to client
        551  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

禁用bloom filter后,虽然仍是访问IM区,consistent gets的个数仍为17,但语句的执行时间已增长为45.18秒.

如果使用hint来禁用IM访问,强制连接在传统的buffer cache中执行又会是什么结果呢:

SQL> select /*+ NO_INMEMORY */ count(*)
from big_table a,small_table b
where a.id=b.id and to_char(b.time,'yyyymmdd')='20140912';
  COUNT(*)
----------
   1000000
Elapsed: 00:00:52.11
-------------------------------------------------------------------------------------------
| Id  | Operation           | Name        | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |             |     1 |    24 |       | 90645   (2)| 00:00:04 |
|   1 |  SORT AGGREGATE     |             |     1 |    24 |       |            |          |
|*  2 |   HASH JOIN         |             |   977K|    22M|    27M| 90645   (2)| 00:00:04 |
|*  3 |    TABLE ACCESS FULL| SMALL_TABLE |   976K|    16M|       |   978   (7)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| BIG_TABLE   |    50M|   286M|       | 44899   (2)| 00:00:02 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("A"."ID"="B"."ID")
   3 - filter(TO_CHAR(INTERNAL_FUNCTION("B"."TIME"),'yyyymmdd')='20140912')
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     162214  consistent gets
          0  physical reads
          0  redo size
        542  bytes sent via SQL*Net to client
        551  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

虽然都是在内存执行的,但这里的consistent gets急剧增长为162214,语句执行时间也增加到了52.11秒.

以上测试结果总结如下表:

Join Method

Consistent Gets

Elapsed Time

1.Join In IM, with BF enabled

17

9.35

2.Join In IM, with BF disabled

17

45.18

2.Join In Buffer Cache

162214

52.11

从测试结果可看到,Oracle IM option通过将数据作列式压缩存储在内存中,并启用bloom filter后,连接查询的效率得到了极大的提升。

关于Bloom Filter

Bloom filter 是由 Howard Bloom 在 1970 年提出的一种计算方法,用于检测一个元素是不是集合中的一个成员。Oracle从10g开始引入该算法。其基本思想就是用一个或多个hash函数对数据集A中的每个成员做hash计算,计算结果映射到一个位向量(bit vector)中。位向量所有位初始值都为0,根据hash结果将位向量中相应位置1。对集合A中的所有成员的hash计算完成后,就得到了该数据集的位向量。当需要判断集合B中的元素是否属于该数据集时,也用相同的hash函数对其映射得到它的位向量,然后将其位向量上所有为1的位与数据集位向量上相应位比较,如果发现数据集的位向量上某个位为0的话,可以判断这个元素不属于该数据集,从而将这些元素排除出去。而如果所有相应位都为1的话,那么该元素可能属于这个数据集A,也可能不属于。也就是说Bloom Filter计算后的结果还不是最终的结果,它能帮助快速排除那些不符合条件的记录,接下来还需要使用其他连接方法来保证最终结果的正确性。

以前面测试结果中的执行计划为例,它首先访问表small_table,建立bloom filter :BF0000,接下来使用该filter对big_table作过滤操作,由于Bloom Filter算法自身的限制,最终还需要使用hash join来保证执行结果的正确性。

---------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |     1 |    24 |  2632  (35)| 00:00:01 |
|   1 |  SORT AGGREGATE               |             |     1 |    24 |            |          |
|*  2 |   HASH JOIN                   |             | 10012 |   234K|  2632  (35)| 00:00:01 |
|   3 |    JOIN FILTER CREATE         | :BF0000     | 10000 |   175K|   114  (54)| 00:00:01 |
|*  4 |     TABLE ACCESS INMEMORY FULL| SMALL_TABLE | 10000 |   175K|   114  (54)| 00:00:01 |
|   5 |    JOIN FILTER USE            | :BF0000     |    50M|   286M|  2115  (21)| 00:00:01 |
|*  6 |     TABLE ACCESS INMEMORY FULL| BIG_TABLE   |    50M|   286M|  2115  (21)| 00:00:01 |
---------------------------------------------------------------------------------------------

 

In Memory 聚合(aggregation)查询:Vector Group By

Oracle在12.1.0.2版本中引入了一种新的查询转换技术,称为Vector Group By, 据称这种算法可以更高效的使用CPU资源。这种转换多应用于数据仓库类型应用的分析查询中,这里就以数据仓库应用中的查询场景来介绍这种查询转换技术的实现:

这种查询转换可分为两个阶段:

Phase 1

1. 扫描“维度表”(dimension tables),也就是小表,根据扫描结果在连接列上建立key vectors,也就是一个一维阵列;

2. 根据key vectors的结果,以及原有的维度表扫描结果,创建一个新的称为IM Accumulator的数据结构。IM Accumulator是一个多维阵列,存放在PGA中。其作用是使得在扫描“事实表”(fact table)期间就可以作聚合或group by计算,而不用等到所有的结果都返回以后;

3. 将“维度表”中涉及到的select列的结果存放在一个临时表中。

Phase 2

4. 扫描“事实表”和前面生成的key vectors,利用key vectors来过滤“事实表”中的记录,将匹配连接条件的结果放到前面的IM Accumulator中。如果该值已存在,则其对应的统计值就会被更新;

5. 最后,对“事实表”的扫描结果会与第3步中的临时表作join,生成最终的结果。

既然说这种算法能够更有效地使用CPU资源,这里就通过测试观察数据库中的CPU used by this session统计指标的变化来验证这一说法。

Oracle在Sample Schema示例模式中有类似于数据仓库应用的测试数据。这里的测试思路如下:

在SH.SALES等表上作如下查询,该查询用以检索出从1999年12月至2000年2月间Florida州所有城市直销形式的每月销售额。

SELECT c.cust_city, t.calendar_quarter_desc, SUM(s.amount_sold) sales_amount 
FROM sh.sales s, sh.times t, sh.customers c, sh.channels ch 
WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id AND s.channel_id = ch.channel_id AND c.cust_state_province = 'FL' AND ch.channel_desc = 'Direct Sales' AND t.calendar_quarter_desc IN ('2000-01', '2000-02','1999-12') 
GROUP BY c.cust_city, t.calendar_quarter_desc;

测试的场景分别为:

1.  常规执行;

2.  使用hint /*+ VECTOR_TRANSFORM */来启用Vector Group By转换;

3.  将查询表载入In Memory区,并用hint /*+ VECTOR_TRANSFORM */启用Vector Group By转换

他们的执行计划分别为:

1.

-----------------------------------------------------------------------------------------
| Id  | Operation                               | Name              | Rows  |Cost (%CPU)|
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                   |   607 |  490   (2)|
|   1 |  HASH GROUP BY                          |                   |   607 |  490   (2)|
|   2 |   NESTED LOOPS                          |                   |   812 |  489   (2)|
|   3 |    NESTED LOOPS                         |                   | 14975 |  489   (2)|
|   4 |     VIEW                                | VW_GBC_13         | 14975 |  487   (1)|
|   5 |      HASH GROUP BY                      |                   | 14975 |  487   (1)|
|   6 |       NESTED LOOPS                      |                   | 43094 |  484   (1)|
|   7 |        NESTED LOOPS                     |                   | 43094 |  484   (1)|
|   8 |         MERGE JOIN CARTESIAN            |                   |   274 |   21   (0)|
|*  9 |          TABLE ACCESS FULL              | CHANNELS          |     1 |    3   (0)|
|  10 |          BUFFER SORT                    |                   |   274 |   18   (0)|
|* 11 |           TABLE ACCESS FULL             | TIMES             |   274 |   18   (0)|
|  12 |         PARTITION RANGE ITERATOR        |                   |       |           |
|  13 |          BITMAP CONVERSION TO ROWIDS    |                   |       |           |
|  14 |           BITMAP AND                    |                   |       |           |
|* 15 |            BITMAP INDEX SINGLE VALUE    | SALES_TIME_BIX    |       |           |
|* 16 |            BITMAP INDEX SINGLE VALUE    | SALES_CHANNEL_BIX |       |           |
|  17 |        TABLE ACCESS BY LOCAL INDEX ROWID| SALES             |   157 |  484   (1)|
|* 18 |     INDEX UNIQUE SCAN                   | CUSTOMERS_PK      |     1 |    0   (0)|
|* 19 |    TABLE ACCESS BY INDEX ROWID          | CUSTOMERS         |     1 |    0   (0)|
----------------------------------------------------------------------------------------- 

2. 

--------------------------------------------------------------------------------------------
| Id  | Operation                          | Name                      | Rows  |Cost (%CPU)|
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |                           |  6130 |  972   (4)|
|   1 |  TEMP TABLE TRANSFORMATION         |                           |       |           |
|   2 |   LOAD AS SELECT                   | SYS_TEMP_0FD9D6677_381357 |       |           |
|   3 |    VECTOR GROUP BY                 |                           |    20 |   19   (6)|
|   4 |     KEY VECTOR CREATE BUFFERED     | :KV0000                   |       |           |
|*  5 |      TABLE ACCESS FULL             | TIMES                     |   274 |   18   (0)|
|   6 |   LOAD AS SELECT                   | SYS_TEMP_0FD9D6678_381357 |       |           |
|   7 |    VECTOR GROUP BY                 |                           |   613 |  426   (1)|
|   8 |     KEY VECTOR CREATE BUFFERED     | :KV0001                   |       |           |
|*  9 |      TABLE ACCESS FULL             | CUSTOMERS                 |  2734 |  425   (1)|
|  10 |   LOAD AS SELECT                   | SYS_TEMP_0FD9D6679_381357 |       |           |
|  11 |    VECTOR GROUP BY                 |                           |     1 |    4  (25)|
|  12 |     HASH GROUP BY                  |                           |     1 |    4  (25)|
|  13 |      KEY VECTOR CREATE BUFFERED    | :KV0002                   |       |           |
|* 14 |       TABLE ACCESS FULL            | CHANNELS                  |     1 |    3   (0)|
|  15 |   HASH GROUP BY                    |                           |  6130 |  523   (5)|
|* 16 |    HASH JOIN                       |                           |  6130 |  522   (5)|
|  17 |     TABLE ACCESS FULL              | SYS_TEMP_0FD9D6678_381357 |   613 |    3   (0)|
|* 18 |     HASH JOIN                      |                           |  6130 |  518   (5)|
|  19 |      MERGE JOIN CARTESIAN          |                           |    20 |    4   (0)|
|  20 |       TABLE ACCESS FULL            | SYS_TEMP_0FD9D6679_381357 |     1 |    2   (0)|
|  21 |       BUFFER SORT                  |                           |    20 |    2   (0)|
|  22 |        TABLE ACCESS FULL           | SYS_TEMP_0FD9D6677_381357 |    20 |    2   (0)|
|  23 |      VIEW                          | VW_VT_0737CF93            |  6130 |  514   (5)|
|  24 |       VECTOR GROUP BY              |                           |  6130 |  514   (5)|
|  25 |        HASH GROUP BY               |                           |  6130 |  514   (5)|
|  26 |         KEY VECTOR USE             | :KV0001                   | 16697 |  514   (5)|
|  27 |          KEY VECTOR USE            | :KV0002                   | 43110 |  514   (5)|
|  28 |           KEY VECTOR USE           | :KV0000                   |   172K|  514   (5)|
|  29 |            PARTITION RANGE SUBQUERY|                           |   918K|  513   (5)|
|* 30 |             TABLE ACCESS FULL      | SALES                     |   918K|  513   (5)|
-------------------------------------------------------------------------------------------- 

3.

-----------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                      | Rows  |Cost (%CPU)|
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                           |  6130 |  313  (14)|
|   1 |  TEMP TABLE TRANSFORMATION            |                           |       |           |
|   2 |   LOAD AS SELECT                      | SYS_TEMP_0FD9D667A_381357 |       |           |
|   3 |    VECTOR GROUP BY                    |                           |    20 |    5  (20)|
|   4 |     KEY VECTOR CREATE BUFFERED        | :KV0000                   |       |           |
|*  5 |      TABLE ACCESS INMEMORY FULL       | TIMES                     |   274 |    4   (0)|
|   6 |   LOAD AS SELECT                      | SYS_TEMP_0FD9D667B_381357 |       |           |
|   7 |    VECTOR GROUP BY                    |                           |   613 |   30  (10)|
|   8 |     KEY VECTOR CREATE BUFFERED        | :KV0001                   |       |           |
|*  9 |      TABLE ACCESS INMEMORY FULL       | CUSTOMERS                 |  2734 |   29   (7)|
|  10 |   LOAD AS SELECT                      | SYS_TEMP_0FD9D667C_381357 |       |           |
|  11 |    VECTOR GROUP BY                    |                           |     1 |    2  (50)|
|  12 |     HASH GROUP BY                     |                           |     1 |    2  (50)|
|  13 |      KEY VECTOR CREATE BUFFERED       | :KV0002                   |       |           |
|* 14 |       TABLE ACCESS INMEMORY FULL      | CHANNELS                  |     1 |    1   (0)|
|  15 |   HASH GROUP BY                       |                           |  6130 |  277  (13)|
|* 16 |    HASH JOIN                          |                           |  6130 |  275  (13)|
|  17 |     TABLE ACCESS FULL                 | SYS_TEMP_0FD9D667B_381357 |   613 |    3   (0)|
|* 18 |     HASH JOIN                         |                           |  6130 |  272  (13)|
|  19 |      MERGE JOIN CARTESIAN             |                           |    20 |    4   (0)|
|  20 |       TABLE ACCESS FULL               | SYS_TEMP_0FD9D667C_381357 |     1 |    2   (0)|
|  21 |       BUFFER SORT                     |                           |    20 |    2   (0)|
|  22 |        TABLE ACCESS FULL              | SYS_TEMP_0FD9D667A_381357 |    20 |    2   (0)|
|  23 |      VIEW                             | VW_VT_0737CF93            |  6130 |  268  (13)|
|  24 |       VECTOR GROUP BY                 |                           |  6130 |  268  (13)|
|  25 |        HASH GROUP BY                  |                           |  6130 |  268  (13)|
|  26 |         KEY VECTOR USE                | :KV0001                   | 16697 |  268  (13)|
|  27 |          KEY VECTOR USE               | :KV0002                   | 43110 |  268  (13)|
|  28 |           KEY VECTOR USE              | :KV0000                   |   172K|  268  (13)|
|  29 |            PARTITION RANGE SUBQUERY   |                           |   918K|  267  (13)|
|* 30 |             TABLE ACCESS INMEMORY FULL| SALES                     |   918K|  267  (13)|
-----------------------------------------------------------------------------------------------

以上执行计划中,蓝色部分即是前面描述的Vector Group By转换中的Phase 1,红色部分即为Phase 2.

测试结果如下表,可以看到,将数据载入IM区并启用Vector Group By转换后,CPU资源使用量急剧下降,可见这种Vector Group By转换查询尤其适用于IM列式存储的数据。统计结果中还有一个有趣的发现,那就是启用Vector Group By后,DB Block Gets和Physical Reads统计指标都是非0值,跟踪发现Physical Reads是来自于对临时文件的读取,因为Phase 1的结果是要写到临时文件中的。而DB Block Gets则猜测是来自于对Key Vector的current mode读取。

测试场景

CPU Time

Consistent Gets

DB Block Gets

Physical Reads

常规执行

118

11884

0

0

启用Vector Group By转换

38

1859

24

3

In Memory,并启用Vector Group By转换 

9

31

24

3

 其实针对这样的查询,在Vector Group By之外,Oracle还有其他的转换技术,那就是star transformation, 这是在8i时期就引入的技术,孰优孰略,可能就要具体问题具体分析了,这里没有作进一步比较。

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

请登录后发表评论 登录
全部评论

注册时间:2013-05-14

  • 博文量
    17
  • 访问量
    273156