聊聊Oracle 11g的Result Cache(一)

startay發表於2016-06-22

 

快取一直是解決資訊、資料訪問效率的一個重要方法。常見的快取包括記憶體、前端特殊資料結構等。從宏觀角度看,快取的本質是利用空間來換取時間的手段。資料資訊雖然都是儲存在系統後端的資料庫或者檔案系統中,但是為了效能常常會將其以一定形式組織到前端,減少訪問過程中的傳遞鏈過程。

 

 

Oracle最常見的Cache就是SGA中的Buffer Cache和Shared Pool。兩者一個是快取資料塊,另一個是快取處理過的執行計劃。一個是避免系統出現的頻繁物理讀過程,一個是避免系統出現的頻繁硬解析過程。在Oracle 11g中,Oracle更近了一步,直接將結果集合快取,推出了Result Cache特性。本篇就介紹一下這個特性。

 

 

1、Result Cache簡述

 

傳統的Oracle資料讀寫操作,都是將資料塊(Data Block)快取到Buffer Cache中。每次SQL語句來了之後,都是從Buffer Cache中檢索資料塊,也要發生邏輯讀。而Result Cache的原理則是更近了一步,是將SQL結果集直接進行儲存,每次SQL語句來了,就直接把結果集合返回回去,連邏輯讀都省去了。

 

在Oracle 11g中,推出了Result Cache這種新特性,對於結果集合進行處理。Result Cache的難點在於兩個方面,一個是結果集合與目標SQL的匹配,另一個是作為冗餘資料的Cache資訊,如何反映資料的最新變化。相同的SQL在不同時候發出來,結果不同如何實現。

 

從Oracle引數中,我們可以一窺Result Cache的功能。

 

 

SQL> show parameter result_cache

 

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

client_result_cache_lag              big integer 3000

client_result_cache_size             big integer 0

result_cache_max_result              integer     5

result_cache_max_size                big integer 2080K

result_cache_mode                    string      MANUAL

result_cache_remote_expiration       integer     0

 

 

 

2、一個簡單的Result Cache示例

 

下面我們來看一個簡單的Result Cache示例,有一個基本的認識。選擇Oracle 11gR2來進行試驗。

 

 

SQL> select * from v$version;

 

BANNER

--------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

PL/SQL Release 11.2.0.1.0 - Production

CORE    11.2.0.1.0  Production

 

 

建立實驗資料表T,確定當前Result Cache功能開啟。

 

 

SQL> create table t as select * from dba_objects;

Table created

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

     72735

 

SQL> show parameter result_cache_max

 

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

result_cache_max_result              integer     5

result_cache_max_size                big integer 2080K

 

 

result_cache_max_size是Result Cache功能開啟的重要引數,只要設定為非零,我們就可以使用Result Cache功能。

 

 

--注意,是SCOTT使用者,而非SYS使用者;

SQL> show user

USER 為 "SCOTT"

 

--第一次執行語句

SQL> select /*+ result_cache */* from t where wner='SCOTT';

已選擇18行。

已用時間:  00: 00: 00.15

 

執行計劃

----------------------------------------------------------

Plan hash value: 1601196873

------------------------------------------------------------------------------

| Id  | Operation          | Name                       | Rows  | Bytes | Cost(%CPU)| Time     |

------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |           |    17 |  3519 |   273  (1)| 00:00:04 |

|   1 |  RESULT CACHE      | gsk6j3p8jdcqa788k6gyfj9tv1 |       |       |     | |*  2 |   TABLE ACCESS FULL| T                          |    17 |  3519 |   273  (1)| 00:00:04 |

-----------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter("OWNER"='SCOTT')

Result Cache Information (identified by operation id):

------------------------------------------------------

   1 - column-count=15; dependencies=(SCOTT.T); parameters=(nls); name="select /*+ result_cache */* from t where wner='SCOTT'"

 

統計資訊

----------------------------------------------------------

        308  recursive calls

          0  db block gets

       1149  consistent gets

          0  physical reads

          0  redo size

       2348  bytes sent via SQL*Net to client

        422  bytes received via SQL*Net from client

          3  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         18  rows processed

 

 

上面三個語句明顯展示了Result Cache功能特點。

 

預設情況下,Oracle Result Cache是不開啟的。只有對使用了Result Cache Hint的情況下,才能讓SQL語句的結果集合進入Result Cache。

 

在第一句SQL中,我們希望篩選出owner=’SCOTT’的結果集合,因為是第一次執行,所以有一定的邏輯讀操作和遞迴SQL查詢。但是,注意此時的執行計劃和我們通常常見的執行計劃有一定的差異。Oracle構造了一個名字為gsk6j3p8jdcqa788k6gyfj9tv1的結果集Cache,並且儲存在其中。這個與我們日常見到的簡單FTS之後就返回結果有些詫異。

 

此時,我們透過v$result_cache_objects可以看到這個物件存在。

 

 

SQL> select id, cache_id, status, name from v$result_cache_objects;

 

        ID CACHE_ID                       STATUS    NAME

---------- ------------------------------ --------- --------------------------------------------------------------------------------

         0 SCOTT.T                        Published SCOTT.T

         1 gsk6j3p8jdcqa788k6gyfj9tv1     Published select /*+ result_cache */* from t where wner='SCOTT'

 

 

v$result_cache_objects可以檢視當前記憶體SGA中快取的結果集合情況。

 

在第二個SQL語句中,我們沒有使用hint,屬於最直接的SQL語句。從執行計劃和統計量的情況看,Oracle的確執行FTS掃描Buffer Cache中的邏輯讀,執行時間大約為0.01s,比第一次執行的0.15s已經有了提升。

 

 

--第二次無Hint執行,反映真實資訊;

SQL> select * from t where wner='SCOTT';

已選擇18行。

 

已用時間:  00: 00: 00.01

 

執行計劃

----------------------------------------------------------

Plan hash value: 1601196873

 

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |    17 |  3519 |   273   (1)| 00:00:04 |

|*  1 |  TABLE ACCESS FULL| T    |    17 |  3519 |   273   (1)| 00:00:04 |

--------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

 

   1 - filter("OWNER"='SCOTT')

 

統計資訊

----------------------------------------------------------

          4  recursive calls

          0  db block gets

       1119  consistent gets

          0  physical reads

          0  redo size

       2348  bytes sent via SQL*Net to client

        422  bytes received via SQL*Net from client

          3  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         18  rows processed

 

 

第三個SQL語句中,我們再次使用了hint。從執行計劃看,使用到了我們第一次生成的那個gsk6j3p8jdcqa788k6gyfj9tv1 Cache物件。實際執行時間下降到基本為0。

 

 

--第三次,純Result Cache應用

SQL> select /*+ result_cache */* from t where wner='SCOTT';

已選擇18行。

 

已用時間:  00: 00: 00.00 –成本幾乎為零;

 

執行計劃

----------------------------------------------------------

Plan hash value: 1601196873

------------------------------------------------------------------------------

| Id  | Operation          | Name                       | Rows  | Bytes | Cost(%CPU)| Time     |

--------------------------------------------------------------------------------------------

 

|   0 | SELECT STATEMENT   |                            |    17 |  3519 |   273

  (1)| 00:00:04 |

 

|   1 |  RESULT CACHE      | gsk6j3p8jdcqa788k6gyfj9tv1 |       |       |

     |          |

|*  2 |   TABLE ACCESS FULL| T                          |    17 |  3519 |   273

  (1)| 00:00:04 |

-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter("OWNER"='SCOTT')

Result Cache Information (identified by operation id):

------------------------------------------------------

   1 - column-count=15; dependencies=(SCOTT.T); parameters=(nls); name="select /

 

*+ result_cache */* from t where wner='SCOTT'"

 

統計資訊

----------------------------------------------------------

          0  recursive calls

          0  db block gets

          0  consistent gets

          0  physical reads

          0  redo size

       2348  bytes sent via SQL*Net to client

        422  bytes received via SQL*Net from client

          3  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         18  rows processed

 

 

最重要的是觀察統計量消耗,徹底消滅了邏輯讀。也就是說,在使用Result Cache的情況下,根本不會有邏輯讀這樣的檢索操作,而是直接將結果返回回去。

 

3、同步問題

 

另一個擔心問題就是同步,如果我們此時增加資料庫表scott的記錄數目,看看Result Cache能不能及時反饋。

 

--資料變化了

SQL> insert into t select * from dba_objects where wner='SCOTT';

18 rows inserted

 

SQL> commit;

Commit complete

 

 

注意,此時cache object立刻反映了這樣的變化。

 

 

SQL> select id, cache_id, status, name from v$result_cache_objects;

 

        ID CACHE_ID                       STATUS    NAME

---------- ------------------------------ --------- --------------------------------------------------------------------------------

         0 SCOTT.T                        Published SCOTT.T

         1 gsk6j3p8jdcqa788k6gyfj9tv1     Invalid   select /*+ result_cache */* from t where wner='SCOTT'

 

 

我們發現,快取物件立刻從Published狀態變化為Invalid狀態,表示失效了。藉助一個檢視,我們可以“猜測”其中的奧妙。

 

 

SQL> select * from v$result_cache_dependency;

 

 RESULT_ID  DEPEND_ID  OBJECT_NO

---------- ---------- ----------

         1          0      77312

 

SQL> select object_name from user_objects where object_id=77312;

 

OBJECT_NAME

-------------------------------------------------------------------------

T

 

 

注意這個名字為依賴關係的檢視,他告訴我們:結果物件1,依賴物件0,這個物件的object_id=77312,而這個id號恰恰就是我們的資料表T。

 

在另一個層面上,我們在v$result_cache_objects中也看到了ID 1和2,恰恰也就印證了這個。我們猜想過程是這樣:在使用Result Cache過程中,Oracle維護了這個結果集合和基礎表那些物件有關係的依賴關係對應。一旦發生了基礎表變化,無論是從資料到內容,都會引發對Result Cache物件的失效過程。從而在下一次使用SQL的時候直接重新查結果。

 

我們證明一下:

 

 

SQL> select /*+ result_cache */* from t where wner='SCOTT';

 

已選擇36行。

 

已用時間:  00: 00: 00.01

執行計劃

----------------------------------------------------------

Plan hash value: 1601196873

---------------------------------------------------------------------------------------------

 

| Id  | Operation          | Name                       | Rows  | Bytes | Cost(

%CPU)| Time     |

--------------------------------------------------------------------------------

 

-----------------

 

|   0 | SELECT STATEMENT   |                            |    17 |  3519 |   279

  (1)| 00:00:04 |

 

|   1 |  RESULT CACHE      | gsk6j3p8jdcqa788k6gyfj9tv1 |       |       |

     |          |

 

|*  2 |   TABLE ACCESS FULL| T                          |    17 |  3519 |   279

  (1)| 00:00:04 |

---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter("OWNER"='SCOTT')

Result Cache Information (identified by operation id):

------------------------------------------------------

   1 - column-count=15; dependencies=(SCOTT.T); parameters=(nls); name="select /

*+ result_cache */* from t where wner='SCOTT'"

統計資訊

----------------------------------------------------------

          1  recursive calls

          1  db block gets

       1057  consistent gets

          0  physical reads

        132  redo size

       3410  bytes sent via SQL*Net to client

        433  bytes received via SQL*Net from client

          4  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         36  rows processed

 

 

第一次執行,返回正確的結果集合,進行邏輯讀。但是Result Cache ID號沒有變化。檢視統計資訊。

 

 

SQL> select id, cache_id, status, name from v$result_cache_objects;

 

        ID CACHE_ID                       STATUS    NAME

---------- ------------------------------ --------- --------------------------------------------------------------------------------

         0 SCOTT.T                        Published SCOTT.T

         3 gsk6j3p8jdcqa788k6gyfj9tv1     Published select /*+ result_cache */* from t where wner='SCOTT'

         1 gsk6j3p8jdcqa788k6gyfj9tv1     Invalid   select /*+ result_cache */* from t where wner='SCOTT'

 

 

一個新的Published物件被建立,id取值為3,但Cache ID值沒有變化。Oracle又建立起一個新的維護依賴關係。

 

 

SQL> select * from v$result_cache_dependency;

 

 RESULT_ID  DEPEND_ID  OBJECT_NO

---------- ---------- ----------

         3          0      77312

 

 

進一步,如果我們變更的不是scott使用者呢?會不會失效?

 

 

--無關SCOTT的資料插入

SQL> insert into t select * from dba_objects where wner='SYS';

30922 rows inserted

 

SQL> commit;

Commit complete

 

SQL> select * from v$result_cache_dependency;

 

 RESULT_ID  DEPEND_ID  OBJECT_NO

---------- ---------- ----------

 

SQL> select id, cache_id, status, name from v$result_cache_objects;

 

        ID CACHE_ID                       STATUS    NAME

---------- ------------------------------ --------- --------------------------------------------------------------------------------

         0 SCOTT.T                        Published SCOTT.T

         1 gsk6j3p8jdcqa788k6gyfj9tv1     Invalid   select /*+ result_cache */* from t where wner='SCOTT'

         3 gsk6j3p8jdcqa788k6gyfj9tv1     Invalid   select /*+ result_cache */* from t where wner='SCOTT'

 

 

雖然我們插入的資料沒有影響到結果集合,但是Cache依然還是失效了。看來這種關係還是很敏感的。也就說明了Result Cache使用的一個前提:目標資料表變化小。

 

下面我們繼續討論Result Cache的一些特性和用途。

 

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

相關文章