12c in memory option學習筆記一_基礎篇

lovestanford發表於2016-04-21

Oracle 12c in memory選件通過在SGA中分配獨立的記憶體區域(In Memory Area),對資料使用列式壓縮儲存來提高查詢效能.

In Memory區的大小由引數inmemory_size控制, 該引數是一個靜態引數, 修改後需要重啟資料庫方可生效.

In Memory記憶體區可分為兩個子池,分別為1M pool和64K pool. 1M pool用於儲存列式資料. 64K pool用於儲存物件的後設資料(metadata)和事務日誌(transaction journal). 1M pool 中分配的記憶體塊的大小為1M的整數倍,64K pool中分配的記憶體塊大小均為64K。

v$inmemory_area檢視有每個pool大小的詳細資訊:

SQL> select * from v$inmemory_area;
POOL                       ALLOC_BYTES USED_BYTES POPULATE_STATUS                CON_ID
-------------------------- ----------- ---------- -------------------------- ----------
1MB POOL                     854589440   10485760 DONE                                0
64KB POOL                    201326592     393216 DONE                                0 

In Memory區的資料裝載(populating)

相對於磁碟或者快閃記憶體,記憶體資源畢竟成本更高,更為有限,所以需要將有限的資源分配給更需要的物件. Oracle新增了一個INMEMORY屬性用於控制物件是否會被裝載到In Memory區, 該屬性可以定義在表 空間,表,分割槽甚至列級. Oracle In Memory白皮書中給出了一些設定物件IMMEMORY屬性的具體用例, 摘錄於此, 以備將來參考:

ALTER TABLESPACE ts_data INMEMORY;
ALTER TABLE sales INMEMORY NO INMEMORY(prod_id);
ALTER TABLE sales MODIFY PARTITION SALES_Q1_1998 NO INMEMORY;

此外,為了控制物件的裝載順序,Oracle定義了5種優先順序來實現精細控制,通過priority子句來選擇.詳細資訊如下:

根據自己的需求,可以選擇以上幾種優先順序中的一種來定義該物件的裝載順序.若未指定priority子句,則其預設值為NONE.

IM物件的裝載是由後臺程式ora_w00*_<sid>完成的.引數inmemory_max_populate_servers控制IM後臺裝載程式的個數, 其預設值為cpu_count的一半. 程式數越多,裝載速度越快,但消耗的資源也更多.

注:實際觀察發現inmemory_max_populate_servers引數並不是對後臺裝載程式ora_w00*_<sid>的硬性限制,它更像是一個最小值的設定,因為實際中發現ora_w00*_<sid>程式數是可以動態增加的。

在物件載入In Memory區後, 可使用no inmemory操作將其從IM區清除

alter table TEST_IM no inmemory;

此外,要注意的是,改變原有的priority(或者memcompress)屬性,也會導致該物件被清除出IM區.比如:

alter table TEST_IM inmemory priority critical;  --原值為none

這裡還要注意的一點是,由於儲存資料塊的大小為1M的整數倍,也就是記憶體分配的最小單位是1M,為了避免空間浪費,Oracle不會將<=64K大小的物件載入In Memory區.這裡做個簡單測試來驗證這一點:

create table test_im(v varchar2(100)) inmemory pctused 1 pctfree 99 tablespace users;
insert into test_im select rpad('a',100,'a') from dual connect by level <=5;
commit;
SQL> select bytes/1024 from dba_segments where segment_name='TEST_IM';
BYTES/1024
----------
        64
SQL> select * from TEST_IM;
…… 
SQL> select * from v$im_segments where segment_name='TEST_IM';
no rows selected

查詢v$im_segments可發現表TEST_IM並不在IM區. 

SQL> insert into test_im select rpad('a',100,'a') from dual;
1 row created.
SQL> commit;
Commit complete.
SQL> select bytes/1024 from dba_segments where segment_name='TEST_IM';
BYTES/1024
----------
       128
SQL> select * from TEST_IM;
……
SQL> select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments where segment_name='TEST_IM';
SEGMENT_NAME         INMEMORY_SIZE      BYTES POPULATE_STATUS
-------------------- ------------- ---------- --------------------
TEST_IM                    1179648     131072 COMPLETED

而當表TEST_IM的大小大於64K時,Oracle才會將其裝載入In Memory區.

In Memory壓縮

Oracle為IM中的物件提供了六種級別的壓縮選項, 可通過MEMCOMPRESS子句來指定, 詳細資訊如下:

通過下例來觀察In Memory列壓縮功能:

SQL> create table test_im_comp tablespace users as select * from dba_objects;
Table created.
SQL> insert into test_im_comp select * from test_im_comp;
92203 rows created.
SQL> /
……
SQL> commit;
Commit complete.
SQL> alter table TEST_IM_COMP inmemory memcompress for query low;
SQL> select count(*) from test_im_comp;
  COUNT(*)
----------
   2950496

之後可以看到開始向In Memory區裝載TEST_IM_COMP表:

select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments;
SEGMENT_NAME                   INMEMORY_SIZE      BYTES POPULATE_
------------------------------ ------------- ---------- ---------
TEST_IM_COMP                         6553600  411041792 STARTED

等待一會, TEST_IM_COMP表裝載完畢,從壓縮前後的大小比對可看出,這裡採用“memcompress for query low”壓縮演算法後TEST_IM_COMP表的壓縮比大約為6:1。

SQL> select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments;
 
SEGMENT_NAME                   INMEMORY_SIZE      BYTES POPULATE_
------------------------------ ------------- ---------- ---------
TEST_IM_COMP                        63373312  411041792 COMPLETED 

通過使用dbms_compression包,也可以預估資料庫物件在採用不同壓縮級別後載入IM區的壓縮比:

set serveroutput on
 
DECLARE
  l_blkcnt_cmp PLS_INTEGER;
  l_blkcnt_uncmp PLS_INTEGER;
  l_row_cmp PLS_INTEGER;
  l_row_uncmp PLS_INTEGER;
  l_cmp_ratio PLS_INTEGER;
  l_comptype_str VARCHAR2(100);
  comp_ratio_allrows NUMBER := -1;
BEGIN
  dbms_compression.get_compression_ratio (
  scratchtbsname => 'USERS',
  ownname => 'SYS',
  objname => 'TEST_IM_COMP',
  subobjname => NULL,
  comptype => dbms_compression.comp_inmemory_query_low,  --該引數控制壓縮級別
  blkcnt_cmp => l_blkcnt_cmp,
  blkcnt_uncmp => l_blkcnt_uncmp,
  row_cmp => l_row_cmp,
  row_uncmp => l_row_uncmp,
  cmp_ratio => l_cmp_ratio,
  comptype_str => l_comptype_str,
  subset_numrows => dbms_compression.comp_ratio_allrows);
  dbms_output.Put_line('The IM compression ratio is '|| l_cmp_ratio);
END;
/
The IM compression ratio is 6

上述指令碼中的comptype選項可從dbmscomp.sql指令碼中獲得,如下:

grep -i comp_inmemory $ORACLE_HOME/rdbms/admin/dbmscomp.sql
COMP_INMEMORY_NOCOMPRESS      CONSTANT NUMBER := 8192;
COMP_INMEMORY_DML             CONSTANT NUMBER := 16384;
COMP_INMEMORY_QUERY_LOW       CONSTANT NUMBER := 32768;
COMP_INMEMORY_QUERY_HIGH      CONSTANT NUMBER := 65536;
COMP_INMEMORY_CAPACITY_LOW    CONSTANT NUMBER := 131072;
COMP_INMEMORY_CAPACITY_HIGH   CONSTANT NUMBER := 262144;

在以上Oracle提供的5種壓縮演算法中,FOR QUERY LOW選項壓縮後的資料查詢效能最佳。它使用的是一些常規的壓縮技術,如“Dictionary Encoding, Run Length Encoding and Bit-Packing”.

其他壓縮技術的壓縮比例更高,但隨之帶來的讀取時解壓成本也更高。 

In Memory區資料的訪問

當訪問In Memory區中的物件時,執行計劃中會出現新的INMEMORY關鍵字:

--------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |     6 |   312 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS INMEMORY FULL| TEST_IM |     6 |   312 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

但這裡要注意的是, 當出現INMEMORY關鍵字時並不表示資料一定是在IM區獲得的. 這裡的INMEMORY只能說明TEST_IM表的INMEMORY屬性已被enable,對該表資料的訪問有可能是從IM區得到的.這裡使用前面的例子來說明這一點:

create table test_im(v varchar2(100)) inmemory pctused 1 pctfree 99 tablespace users;
insert into test_im select rpad('a',100,'a') from dual connect by level <=5;
commit;
SQL> select bytes/1024 from dba_segments where segment_name='TEST_IM';
BYTES/1024
----------
        64
SQL> select * from TEST_IM;
…… 
SQL> select * from v$im_segments where segment_name='TEST_IM';
no rows selected
SQL> set autotrace traceonly
SQL> select * from test_im;
Elapsed: 00:00:00.03
Execution Plan
----------------------------------------------------------
Plan hash value: 2761107969
--------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |     5 |   260 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS INMEMORY FULL| TEST_IM |     5 |   260 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          7  consistent gets
          0  physical reads
          0  redo size
        800  bytes sent via SQL*Net to client
        550  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          5  rows processed

這裡可以看到,TEST_IM表並不在IM區,但執行計劃顯示得是TABLE ACCESS INMEMORY FULL,說明執行計劃並不能作為判斷是否是從IM區獲取資料的依據.

隨著IM功能的引入,Oracle增加了一些新的統計資訊用於IM相關的統計,這裡可以使用IM scan rows來統計IM訪問的情況,繼續上面的例子:

SQL> select name,value from v$mystat, v$statname where v$mystat.statistic# = v$statname.statistic# and v$statname.name = 'IM scan rows';
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
IM scan rows                                                              6 
SQL> select * from test_im;
V
----------------------------------------------------------------------------------------------------
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 
SQL> select name,value from v$mystat, v$statname where v$mystat.statistic# = v$statname.statistic# and v$statname.name = 'IM scan rows';
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
IM scan rows                                                              6

IM scan rows統計值前後沒有變化,可見這裡的確沒有IM訪問.

SQL> insert into test_im select rpad('a',100,'a') from dual;
1 row created.
SQL> commit;
Commit complete.
SQL> select * from TEST_IM;
……
SQL> select SEGMENT_NAME,INMEMORY_SIZE,BYTES,POPULATE_STATUS from v$im_segments where segment_name='TEST_IM';
SEGMENT_NAME         INMEMORY_SIZE      BYTES POPULATE_STATUS
-------------------- ------------- ---------- --------------------
TEST_IM                    1179648     131072 COMPLETED 
SQL> set autotrace traceonly
SQL> select * from test_im;
6 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2761107969
--------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |     6 |   312 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS INMEMORY FULL| TEST_IM |     6 |   312 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        805  bytes sent via SQL*Net to client
        550  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          6  rows processed
SQL> select name,value from v$mystat, v$statname where v$mystat.statistic# = v$statname.statistic# and v$statname.name = 'IM scan rows';
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
IM scan rows                                                             12 

IM scan rows值增加了6, 說明這裡的資料是從IM區獲得的, 並且行數也完全吻合.

此外,觀察autotrace的結果可發現,在使用了IM訪問後,consistent gets也從之前的7降到了3。 

IMCU(In Memory Compression Units)

IMCU類似於表空間中extent的概念,是列資料在IM區中記憶體分配塊的大小。後臺程式ora_w00*在裝載資料時,會分配自己的IMCU,並將分配給該程式的資料載入到該IMCU中。此後,當訪問IM區中的列資料時,我們在統計資訊中看到的consistent gets值也就是統計所訪問IMCU的個數和所需訪問metadata塊的個數之和。特定物件所分配IMCU的詳細資訊,可從檢視V_$IM_HEADER中查詢。而其metadata塊的資訊可從檢視V_$IM_SMU_CHUNK和V_$IM_SMU_HEAD查詢。

在In Memory內部,以IMCU為單位,Oracle維護了一個In Memory Storage Index,記錄IMCU單元中該列的最大值,最小值。此外,Oracle也會在metadata區為每個IMCU建立相應的metadata dictionary, metadata資訊中會有一些列的統計資訊。檢視V_$IM_COL_CU可以幫助查詢這些metadata dictionary資訊。 

IM相關的檢視:

SQL> SELECT VIEW_NAME FROM DBA_VIEWS WHERE VIEW_NAME LIKE 'V_$IM%';
VIEW_NAME
------------------------------------------------------------------------------------
V_$IM_SEGMENTS_DETAIL              --記錄IM段物件的詳細儲存屬性
V_$IM_SEGMENTS                     --記錄IM段物件的儲存屬性
V_$IM_USER_SEGMENTS                         --記錄當前使用者下IM段物件的儲存屬性
V_$IM_TBS_EXT_MAP                  --記錄IM1M子池物件的區間對映關係
V_$IM_SEG_EXT_MAP                  --記錄IM區物件的所有區間對映關係
V_$IM_HEADER                       --記錄IM區物件所分配IMCU的詳細資訊
V_$IM_COL_CU                       --記錄IMCU中基於列的統計資訊
V_$IM_SMU_HEAD
V_$IM_SMU_CHUNK
V_$IM_COLUMN_LEVEL                          --記錄IMCU中物件的列級壓縮屬性,若未在列級定義,則改檢視為空
10 rows selected.
--以上對各VIEW的註釋來自測試中的觀察,描述有可能欠準確。 

IM相關的等待事件:

SQL> select name from v$event_name where name like '%IM %';
NAME
----------------------------------------------------------------
IM buffer busy
enq: IM - contention for blr
IM CU busy
latch: IM area scb latch
latch: IM area sb latch
latch: IM seg hdr latch
latch: IM emb latch
enq: SY - IM populate by other session
IM populate completion
9 rows selected 

IM相關的統計資訊:

SQL> select name from v$statname where name like 'IM %' order by 1;
NAME
----------------------------------------------------------------
IM fetches by rowid from IMCU
IM fetches by rowid from disk
IM fetches by rowid from fetch list
IM fetches by rowid from journal
IM fetches by rowid row invalid in IMCU
IM populate (faststart) CUs accumulated write time (ms)
IM populate (faststart) CUs bytes read
……
198 rows selected.

 

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/21754115/viewspace-2085247/,如需轉載,請註明出處,否則將追究法律責任。

相關文章