引數 session_cached_cursors 與 open_cursors詳解

xz43發表於2010-12-20

一、OPEN_CURSORS

    就是一個session同時開啟的cursor的數量上限。

   確定open_cursors
    select max(count(*)) max_cacheable_cursors
      from (select p.kglobt18 schema# -- parsing schema number
              from sys.x$kglcursor p
             where p.kglobt12 > 2 -- enough parse_calls
            union all
            select s.kglntsnm schema# -- authorized schema number
              from sys.x$kglcursor c, sys.x$kglsn s
             where c.kglobt12 > 2
               and s.kglhdadr = c.kglhdadr)
     group by schema#

二、SESSION_CACHED_CURSORS

     就是說一個session可以快取多少個cursor,讓後續相同的SQL語句不再開啟遊標,從而避免軟解析的過程來提高效能。(繫結變數是解決硬解析的問題),軟解析同硬解析一樣,比較消耗資源。所以這個引數非常重要

    oracle有一個概念,那就是session cursor cache,中文描述就是有一塊記憶體區域,用來儲存關閉了的cursor。當一個cursor關閉之後,oracle會檢查這個cursor的request次數是否超過3次,如果超過了三次,就會放入session cursor cache,這樣在下次parse的時候,就可以從session cursor cache中找到這個statement, session cursor cache的管理也是使用LRU。

    session_cached_cursors這個引數是控制session cursor cache的大小的。session_cached_cursors定義了session cursor cache中儲存的cursor的個數。這個值越大,則會消耗的記憶體越多。

    另外檢查這個引數是否設定的合理,可以從兩個statistic來檢查。

SQL> select name,value from v$sysstat where name like '%cursor%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
opened cursors cumulative                                          21577316
opened cursors current                                                 2399
session cursor cache hits                                           9992832
session cursor cache count                                             8254
cursor authentications                                               434123

SQL> select name,value from v$sysstat where name like '%parse%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
parse time cpu                                                      1400526
parse time elapsed                                                 13979044
parse count (total)                                                20982181
parse count (hard)                                                  1396983
parse count (failures)                                                10958

session cursor cache hits 和parse count(total) 就是總的parse次數中,在session cursor cache中找到的次數。所佔比例越高,效能越好。如果比例比較低,並且有剩餘記憶體的話,可以考慮加大該引數。

    Oracle 9i及以前,該引數預設是0,10G上預設是20。

    設定pga端的cache list的長度,當session_cached_cursors設定為0時,pga的cache list長度為0,這時候當sga中的cursor關閉的時候它相關的library cache handle的lock位被清0,從v$open_cursor裡看不到這個被關閉的cursor,它服從於shared pool的lru機制,當shared pool需要新的buffer空間時,它將會被flush出shared pool。當session_cached_cursors設定為非0值時,pga的cache list長度為session_cached_cursors值的大小,同時pga cache list將會保留一份複製,這時候即使sga中的cursor關閉的時候它相關的library cache handle始終被加了null mode lock,當shared pool空間緊張時library cache handle始終將會被保留在shared pool中,而新的應用訪問這個cursor的時候會直接去自己的pga cache list裡面搜尋。

    cursor_space_for_time:

    當設定了session_cached_cursors為非0值後,如果cursor_space_for_time值被設為false,那麼當shared pool空間緊張時,雖然library cache handle不會被flush出去,但是它指向的library cached object(lco,其中包含了handle和children handle的地址,許可權,型別,狀態,指向kgl block的指標,其中kgl block包含了真正的程式碼段和執行計劃等內容)將會被flush出去而不管它相關的cursor有沒關閉,如果需要lco的時候將要reloads。如果cursor_space_for_time值被設為true,那麼當cursor在開啟狀態下,handle指向的lco將不會被flush出shared pool,這樣就可以降低reloads出現的頻率。不過對於sql共享做的不好的資料庫,設定cursor_space_for_time將會帶來一些問題,share pool可能會出現04031的錯誤。

    gets:

    當試圖parse一句sql時,oracle要先獲得一個handle,在handle上載入一個lock,gets表示handle request times。

    pin:

    當獲得handle後,定位到lco,然後pin住lco使它在被執行的時候不被flush出去。

 

二、引用ASKTOM相關內容如下

Hi Tom
 Can you explain briefly the difference between soft and hard parse?
 你能不能給我詳細介紹一下軟解析與硬解析的區別啊?
and we said...
 Here is a long winded answer, it is extracted in part from a new book oming out soon "beginning Oracle programming" that I collaborated on:
 這是一個很長的解釋,是從我與幾個同事一起合著的新書<>中提取出來的.

 Parsing
 解析
 This is the first step in the processing of any statement in Oracle.  Parsing is the act of breaking the submitted statement down into its component parts ? determining what type of statement it is (query, DML, DDL) and performing various checks on it. 
 這是Oracle處理所有語句的第一步,解析就是將使用者提交的語句(根據一定的規則)分成不同的元件,來確定這條語句是什麼型別的語句(query,查詢,DML,資料操縱語言,DDL還是資料定義語言),以對這條語句執行各種不同的語法檢測.
 The parsing process performs two main functions:
 解析過程主要有一下兩個功能.
 1. Syntax Check: is the statement a valid one?  Does it make sense given the SQL grammar documented in the SQL Reference Manual.  Does it follow all of the rules for SQL.
    語法檢查,這條語句是否合理? 它是否符合SQL語法文件<>的相關語法,是不是符合SQL語句的所有規則.
 2. Semantic Analysis:  Going beyond the syntax ? is the statement valid in light of the objects in the database (do the tables and columns referenced exist).  Do you have access to the objects ? are the proper privileges in place?  Are there ambiguities in the statement ? for example if there are two tables T1 and T2 and both have a column X, the query ?select X from T1, T2 where ?? is ambiguous, we don?t know which table to get X from.  And so on.
    語義分析: 已經處理完語法檢查了嗎? 這條語句是否正確的引用了資料庫中物件(是否語句中所有的表和欄位都存在). 是否有訪問這些物件的許可權? 是不是對應的許可權都有效(主要指role對應的許可權吧,)? 比如是否有如下的兩個表T1,T2,而這兩個表有相同的欄位名column X,而查詢語句 ? select X from T1,T2 where ??,(沒有明確指定列名),我們無法知道從哪個表去取出欄位X的值,以及類似的一系列問題.
   
 So, you can think of parsing as basically a two step process, that of a syntax check to check the validity of the statement and that of a semantic check ? to ensure the statement can execute properly.  The difference between the two types of checks are hard for you to see ? Oracle does not come back and say ?it failed the syntax check?, rather it returns the statement with a error code and message.  So for example, this statement fails with a syntax error:
 因此,你可以認為解析基本上是由這兩個步驟組成的: 檢查語句有效性的語法檢查和確保語句能夠正常執行的語義檢查.這兩種檢查的區別就是你所要說的硬解析了.Oracle不會特別的指出這條語句沒有透過語法檢查,它給你返回這條語句對應的錯誤程式碼和錯誤資訊. 比如: 下面這條語句沒有透過語法檢查.複製內容到剪貼簿程式碼:
 SQL> select from where 2;
 select from where 2
         *
 ERROR at line 1:
 ORA-00936: missing expression
 
 While this statement failed with a semantic error ? if the table NOT_A_TABLE existed and we had permission to access it, this statement would have succeeded:
 而這條語句沒有透過語義檢查? 如果表NOT_A_TABLE存在,並且我們具有訪問這個表的許可權,這條語句就會正確執行了.複製內容到剪貼簿程式碼:
 SQL> select * from not_a_table;
 select * from not_a_table
               *
 ERROR at line 1:
 ORA-00942: table or view does not exist
 That is the only way to really tell the difference between a semantic and syntactic error ? if the statement COULD have executed given the proper objects and privileges, you had a semantic error, otherwise if the statement could not execute under any circumstances, you have a syntax error.  Regardless ? Oracle will not execute the statement for you!
 只是我能用來解釋語法錯誤和語義錯誤的唯一的辦法了.如果在賦予合適的物件和許可權的情況下,這條語句確實能夠執行的話,我們稱之為語義錯誤,否則,如果這條語句在任何條件下都不能執行的話,我們就稱之為語法錯誤.無論如何,Oracle都不會為你執行這條語句.
 The next step in the parse operation is to see if the statement we are currently parsing has already in fact been processed by some other session.  If it has ? we may be in luck here, we can skip the next two steps in the process, that of optimization and row source generation.  If we can skip these next two steps in the process, we have done what is known as a Soft Parse ? a shorter process to getting our query going.  If we cannot, if we must do all of the steps, we are performing what is known as a Hard Parse ? we must parse, optimize, generate the plan for the query.  This distinction is very important.  When developing our applications we want a very high percentage of our queries to be Soft Parsed ? to be able to skip the optimize/generate phases ? as they are very CPU intensive as well as a point of contention (serialization).  If we have to Hard Parse a large percentage of our queries, our system will function slowly and in some cases ? not at all.
 解析中的下一個步驟就是檢查我們現在正在解析的語句是否已經被其他的session執行過,在這一步我們可能很幸運,我們可以跳過下面的兩個步驟: 語句的最佳化(生成執行計劃) 和 生成執行編碼. 如果我們能夠跳過這兩個步驟,我們就實現了一般意義上的軟解析, 我們必須解析,最佳化,併為這條語句生成執行計劃.這個特性(distinction)非常重要,當我們開發應用程式的時候,我們會希望大部分的語句都是軟解析的,以跳過最佳化和生成編碼的步驟,因為他們和爭用(序列化,)(contention)一樣,都是非常消耗CPU的操作.如果我們必須硬解析我們語句的大部分的話,在某些情況下我們的系統就會執行的很慢,並不是任何時候(在olap,dss系統應該是個例外吧)
 The way this sharing of SQL in Oracle is accomplished is via the shared pool, a piece of memory in the SGA maintained by Oracle.  We covered this topic in chapter 5 but will revisit it again in the context of processing a query.  After Oracle parses the query and it passes the syntax and semantic checks ? it will look in the shared pool component of the SGA to see if that same exact query has already been processed by another session.  Since it has performed the semantic check it has already figured out:
 在Oracle中共享SQL的方式是透過共享池--SGA中的一塊記憶體(由Oracle系統來維護)--實現的。我們在第五章中討論了這個主題,並且會在查詢處理的章節再次討論這個主題,當Oracle已經解析了這條語句,並且已經透過語法和語義檢查的時候,Oracle就會在SGA的shared pool(共享池)元件中查詢,看看是否已經有一個完全一樣的語句已經被另外一個session執行過。因為語句已經透過了oracle的語義檢查,並且oracle已經計算出:
o Exactly what tables are involved
具體涉及到哪些表.
o That we have access to the tables (the proper privileges are there)
我們擁有訪問對應表的個相應許可權.等等...
And so on.  Now, it can look at all of the queries in the shared pool that have already been parsed/optimized and generated to see if the work has already been done. 
現在,可以在所有已經經過解析和最佳化並且生成的可執行編碼的SQL語句搜尋,來看看對應的語句是否已經經過解析。
軟解析與session_cashed_cursor引數
can you explain what means the default value (0) of session_ casched_cursor parameter? Oracle always has a cache of sqls in sga and size of this cache is determinated through init.ora parameter shared_pool_size.
tom:你能不能給我解釋一下,session_cashed_cursor(預設值為0)引數的含義,Oracle在SGA中始終保持一個sql語句的快取,這個快取的大小是由初始化引數shared_pool_size的大小決定的。
Followup: 
The default value of ZERO means that by default, ZERO cursors will be cached for your session.
預設值為0表示,在預設情況下,Oracle不會給你的session快取遊標.
They will be cached in the shared pool -- but your session will have to find
them there.  Session Cached Cursors can remove the need to have to "ind" them.  Consider this example that shows the difference spent when soft parsing without session cached cursors vs soft parsing WITH session cached cursors:
他們會被快取在共享池(Shared Pool)中,但是你的session必須在共享池中去查詢他們,session cached cursors可以省略掉去共享池中查詢這一步。下面這個例子給你演示了沒有快取遊標和快取了遊標之後軟解析之間的差別和系統消耗資源的情況
ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> create table emp as select * from scott.emp;
Table created.
ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );
Table created.
ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;
View created.

ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> column name format a40
ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> declare
  2      l_start number;
  3          l_cnt   number;
  4  begin
  5      execute immediate 'alter session set session_cached_cursors=0';
  6      insert into run_stats select 'before', stats.* from stats;
  7 
  8      l_start := dbms_utility.get_time;
  9      for i in 1 .. 1000
10      loop
11            execute immediate 'select count(*) from emp' into l_cnt;
12      end loop;
13      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
14 
15      execute immediate 'alter session set session_cached_cursors=100';
16      insert into run_stats select 'after 1', stats.* from stats;
17 
18      l_start := dbms_utility.get_time;
19      for i in 1 .. 1000
20      loop
21            execute immediate 'select count(*) from emp' into l_cnt;
22      end loop;
23      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
24 
25      insert into run_stats select 'after 2', stats.* from stats;
26  end;
27  /
45 hsecs
35 hsecs
PL/SQL procedure successfully completed.so, session cached cursors RAN faster (i ran this a couple of times, there
were no hard parses going on.  But the real good news is:
因此,在session中快取遊標可以執行的更快(我執行了好幾次,沒有硬解析的發生),真正可喜的訊息是:
ops$[email]tkyte@ORA817DEV.US.ORACLE.COM[/email]> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
10     and (c.value-b.value) <> (b.value-a.value)
11   order by abs( (c.value-b.value)-(b.value-a.value))
12  /
NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
LATCH.checkpoint queue latch                      3          4          1
LATCH.redo allocation                            30         31          1
STAT...consistent gets                         5088       5089          1
STAT...deferred (CURRENT) block cleanout          2          3          1
applications
STAT...calls to get snapshot scn: kcmgss       5019       5018         -1
STAT...enqueue releases                          10          9         -1
STAT...execute count                           1015       1014         -1
STAT...opened cursors cumulative               1015       1014         -1
STAT...parse count (total)                     1015       1014         -1
STAT...session cursor cache count                 0          1          1
STAT...redo entries                              28         27         -1
STAT...recursive calls                         1180       1179         -1
STAT...physical reads                             1          0         -1
LATCH.direct msg latch                            2          0         -2
LATCH.session queue latch                         2          0         -2
LATCH.done queue latch                            2          0         -2
STAT...free buffer requested                      8          6         -2
STAT...enqueue requests                          11          9         -2
LATCH.messages                                    3          0         -3
STAT...db block changes                          47         44         -3
LATCH.redo writing                                3          0         -3
LATCH.ksfv messages                               4          0         -4
STAT...session logical reads                  17128      17123         -5
LATCH.row cache objects                         184        178         -6
STAT...db block gets                          12040      12034         -6
STAT...parse time elapsed                         9          3         -6
STAT...parse time cpu                            13          4         -9
STAT...recursive cpu usage                       51         38        -13
LATCH.cache buffers chains                    34315      34335         20
STAT...redo size                              23900      24000        100
STAT...session cursor cache hits                  3       1002        999
LATCH.shared pool                              2142       1097      -1045
LATCH.library cache                           17361       2388     -14973
34 rows selected.

see the significantly REDUCED number of LATCH counts on the library and shared pool.  Since a latch is a lock, a lock is a serialization device, serialization
implies WAITS -- using the session cached cursors will increase scalability and performance as you add more and more users.  Its not only FASTER, but more scalable as well...
看到庫快取和共享池中鎖存(Latch)數目的明顯的下降了嗎,因為鎖存(Latch)就是鎖(Lock),而鎖(Lock)就是隻能序列執行的裝置,序列就意味著等待--使用session快取的遊標可以提高系統的擴充套件性(scalability)和效能,當你的系統的使用者數不斷增加的時候,它不僅可以執行的更加快捷,而且可以提高了系統的擴充套件性。




 

   

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

相關文章