11G result cache新特性的更多深入研究

viadeazhu發表於2009-09-29

  前面一個帖子,探討了一些11G result cache的一些可能沒被人所注意到的一些發現。

http://space.itpub.net/15415488/viewspace-615398

而這個帖子是在經過team meeting大家提出的疑問之後,

而對result cache做出的一些更加深入的研究和分析。

 

在team meeting中同事提出了一些問題,而這裡先對這些問題做出一些解答。

--------------FAQ Part---------------

Q1:更新其他的行、或者更新其他block中的行 會不會invalidate 這個result cache?

A1:會。任何DML/DDL(甚至包括grant)都會使基於這個object的result cache invalidate。

所以說,result cache他的依耐性是object level的,既不是row level的,也不是block level的。

這樣的話,我認為result cache只有對那些在平時幾乎沒有任何DML的只讀表比較有用。

 

Q2:result cache使用什麼latch保護?是不是用的shared pool latch?

A2:在平時讀取階段不是使用的shared pool latch,而是使用的result cache latch。

SQL> select * from v$latchname where name like 'Result Cache%';

    LATCH# NAME                                 HASH
---------- ------------------------------ ----------
       373 Result Cache: Latch            1545889529
       374 Result Cache: SO Latch          986859868

 

Q3:result cache有enqueue/lock保護麼?

A3:是的。RC enqueue就是拿來保護併發修改的。

例如當我們建立一個新的result cache時。

SQL>  select distinct ksqsttyp,ksqstexpl from x$ksqst where KSQSTEXPL like '%result%' order by ksqsttyp;

KS
--
KSQSTEXPL
--------------------------------------------------------------------------------
RC
Coordinates access to a result-set

 

Q4:使用result cache有什麼overhead麼?

A4:我做了一個簡單的實驗來得到他的overhead:

SQL> set timing on
SQL> exec sys.runstats_pkg.rs_start;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.19


-- 下面的PLSQL不使用result cache來查詢100000次。
SQL>  DECLARE
  2      v_name varchar2(100);
  3   BEGIN
    FOR i IN 1 .. 100000 LOOP
        select name into v_name from testbyhao where id=1000;
    END LOOP;
 END;
 /  4    5    6    7    8 

PL/SQL procedure successfully completed.

Elapsed: 00:00:10.31
SQL> exec sys.runstats_pkg.rs_middle;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.02

--下面的PLSQL我使用result cache來查詢100000次。

SQL>  DECLARE
  2      v_name varchar2(100);
  3   BEGIN
    FOR i IN 1 .. 100000 LOOP
        select /*+result_cache*/ name into v_name from testbyhao where id=1000;
    END LOOP;
 END;
 /  4    5    6    7    8 

PL/SQL procedure successfully completed.

Elapsed: 00:00:08.85
--然後下面對比兩種方式的statistics的差異(只取差值>1000的)

SQL> exec sys.runstats_pkg.rs_stop(1000);
Run1 ran in 1565 hsecs
Run2 ran in 1603 hsecs
run 1 ran in 97.63% of the time
       
Name                                  Run1        Run2        Diff
STAT...session uga memory            8,144       3,880      -4,264
STAT...calls to kcmgrs             100,044          54     -99,990
STAT...rows fetched via callba     100,000           2     -99,998
STAT...table fetch by rowid        100,000           2     -99,998
STAT...index fetch by key          100,000           2     -99,998
LATCH.Result Cache: Latch                0     200,003     200,003
STAT...session logical reads       300,035          44    -299,991
STAT...consistent gets             300,011          19    -299,992
STAT...consistent gets from ca     300,011          19    -299,992
STAT...consistent gets - exami     300,005          12    -299,993
STAT...buffer is not pinned co     300,000           6    -299,994
       
Run1 latches total versus runs -- difference and pct
Run1        Run2        Diff       Pct
1,349     201,389     200,040       .67%

PL/SQL procedure successfully completed.


我們可以很清楚地看到,在僅僅全部讀取result cache的情況下,

所帶來的overhead只在於更多的result cache latch。

而這個result cache latch他到底有多少個呢?

答案是1個,因為他們沒有latch children。

SQL> select count(*)                  
  2   from v$latch_children           
  3   where name='Result Cache: Latch';
                                      
  COUNT(*)                            
----------                            
         0  

--------------End of FAQ Part---------------

--------------Viadea's分割線-----------------

-------------Other Finding Part--------------

經過閱讀國外很多牛人的實驗方法,再透過自己切身實驗,得到更多更深入的finding,

於是姑且將一些精華的東西分享給大家。

1.Trace result cache的event是:

ALTER SESSION SET EVENTS '43905 trace name context forever, level 1';   

得到的結果類似於:

*** 2009-09-28 06:45:46.101
Objects for this node : 24006
-------------------------------------
Query[len=44]: SELECT /*+result_cache*/ NAME FROM TESTBYHAO
Normalized Query[len=25]: 172 670 70 1?"TESTBYHAO" 
Cache id1: at33rkq5njw4z12qkkp17rwctj
Cache id2: at33rkq5njw4z12qkkp17rwctj
Column count: 1
NLS Dependent: NO  User Referenced: NO  Ordered: NO  Auto: NO
Dependencies: (24006 - HAOZHU_USER.TESTBYHAO)

 

2.result cache 的background process是:

SQL> select name,description from V$BGPROCESS where name like 'RC%';

NAME  DESCRIPTION
----- ----------------------------------------------------------------
RCBG  Result Cache: Background

 

3.每次存取(讀) result cache會得到2次latch。

SQL> select gets, misses, sleeps, wait_time               
  2   from v$latch                 
  3   where name = 'Result Cache: Latch';    

      GETS     MISSES     SLEEPS  WAIT_TIME
---------- ---------- ---------- ----------
    601597          0          0          0

Elapsed: 00:00:00.01
SQL>  DECLARE
  2      v_name varchar2(100);
  3   BEGIN
    FOR i IN 1 .. 100000 LOOP
        select /*+result_cache*/ name into v_name from testbyhao where id=1000;
    END LOOP;
 END;
 /  4    5    6    7    8 

PL/SQL procedure successfully completed.

Elapsed: 00:00:08.77
SQL>  select gets, misses, sleeps, wait_time  
  2    from v$latch                           
  3    where name = 'Result Cache: Latch';    

      GETS     MISSES     SLEEPS  WAIT_TIME
---------- ---------- ---------- ----------
    801597          0          0          0

Elapsed: 00:00:00.01
SQL>
SQL> select (801597-601597)/100000 from dual;

(801597-601597)/100000
----------------------
                     2


 

4.每次修改(例如新建一個result cache)會得到4次latch:

SQL> select count(*) from testbyhao;

  COUNT(*)
----------
      4000
SQL> select gets, misses, sleeps, wait_time   
  2   from v$latch                            
  3   where name = 'Result Cache: Latch'; 

      GETS     MISSES     SLEEPS  WAIT_TIME
---------- ---------- ---------- ----------
    817699          0          0          0

Elapsed: 00:00:00.01
SQL> DECLARE
  2      v_name varchar2(100);
  3  begin
 for cur in (select * from testbyhao)            
  loop                                                        
   select /*+result_cache*/ name into v_name from testbyhao where id=cur.id;                                                      
end loop;
end;  4    5    6    7    8 
  9  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.75
SQL> select gets, misses, sleeps, wait_time   
  2   from v$latch                            
  3   where name = 'Result Cache: Latch'; 

      GETS     MISSES     SLEEPS  WAIT_TIME
---------- ---------- ---------- ----------
    833698          0          0          0

Elapsed: 00:00:00.01
SQL> select (833698-817699)/4000 from dual;

(833698-817699)/4000
--------------------
             3.99975

 

5.重要引數_result_cache_timeout和enq: RC - Result Cache: Contention的關係。

_result_cache_timeout預設值是60s,代表著一個session等待得到result cache多少秒。

下面的例子非常經典,我拿來主義自己測了一把。

--第一個session我讓他試圖建立一個result cache,但讓他一直保持“NEW”的狀態。

--這樣其他session的同一條語句就沒法獲得這個enqueue。

Session A:
SQL> variable rc refcursor;
SQL> exec open :rc for select /*+ result_cache */ id from testbyhao2;

PL/SQL procedure successfully completed.

SQL> set serveroutput on
SQL> exec rc_fetch(:rc);
1469

PL/SQL procedure successfully completed.

--我們可以看到Session A拿到了這個RC enqueue,沒有釋放.
SQL> select sid, type, id1, id2, lmode
  2  from v$lock
where sid=sys_context('userenv', 'sid');  3 

       SID TY        ID1        ID2      LMODE
---------- -- ---------- ---------- ----------
       318 RC          1          1          6
       318 AE         99          0          4

--從v$result_cache_objects,可以看到當前這個result cache的狀態時NEW,並不是published。

SQL> /

STATUS    NAME                                                                                                                              PIN_COUNT SCAN_COUNT
--------- -------------------------------------------------------------------------------------------------------------------------------- ---------- ----------
CACHE_ID
---------------------------------------------------------------------------------------------
CACHE_KEY
---------------------------------------------------------------------------------------------
Published HAOZHU_USER.TESTBYHAO2                                                                                                                    0          0
HAOZHU_USER.TESTBYHAO2
HAOZHU_USER.TESTBYHAO2

New       SELECT /*+ result_cache */ ID FROM TESTBYHAO2                                                                                             2          0
0632rwbj2n03pfp9gymdv0ck68
6s5papz2k5hw4dkgus2h8yx9jr


Session B:
--從Session B再run同一條語句,而這條語句會試圖得到RC enqueue
SQL> set serveroutput on
SQL> set timing on
SQL> variable rc refcursor;
SQL> exec open :rc for select /*+ result_cache */ id from testbyhao2;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.01
SQL> exec rc_fetch(:rc);
1469

PL/SQL procedure successfully completed.

Elapsed: 00:01:01.38
--我們可以看到Session B在執行如此簡單的一條語句的時候居然等待了60s,

--而這個時間就是我們的_result_cache_timeout。

--當Session B沒能獲得他所希望的RC enqueue之後,他就會Bypass這個result cache,

--然後自己使用正常的執行方式獲得想要的答案。 

--在Session B等待的這60s中,我看到了所希望看到的result cache contention。
TEST > ora10 active
  9:16am  up 264 day(s), 11:19,  10 users,  load average: 0.34, 0.32, 0.32

   SID USERNAME   MACHINE                        EVENT                          PARAM                   W   WT SQL                      ST     LT LOGON_TIME STATE
------ ---------- ------------------------------ ------------------------------ -------------------- ---- ---- ------------------------ -- ------ ---------- -------------------
   636 HAOZHU_USE                  enq: RC - Result Cache: Conten 1380122628/1/1          0    4 2393857412/1029988163    A      15         53 WAITING

SQL> / 

STATUS    NAME                                                                                                                              PIN_COUNT SCAN_COUNT
--------- -------------------------------------------------------------------------------------------------------------------------------- ---------- ----------
CACHE_ID
---------------------------------------------------------------------------------------------
CACHE_KEY
---------------------------------------------------------------------------------------------
Published HAOZHU_USER.TESTBYHAO2                                                                                                                    0          0
HAOZHU_USER.TESTBYHAO2
HAOZHU_USER.TESTBYHAO2

Bypass    SELECT /*+ result_cache */ ID FROM TESTBYHAO2                                                                                             1          0
0632rwbj2n03pfp9gymdv0ck68
6s5papz2k5hw4dkgus2h8yx9jr

所以,result cache作為一個足夠吸引人的new feature,也帶來了一個新的現象:

Readers blocks Readers。

為了降低這種block的機率,DBA可以根據自己一段時間的monitor的資料,適當的降低_result_cache_timeout的大小。

 

6.為了感謝各位的捧場,特地再奉上一本講result cache internal的PPT。

裡面主要講解了result cache的記憶體結構、演算法的更加深入的東西。

喜歡研究更細緻的同學可以看看。

 

----------End of Other Finding Part----------

 

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

相關文章