Library Cache 結構及記憶體管理 [final]

路途中的人2012發表於2016-01-20

Library cache是Shared pool的一部分,它幾乎是Oracle記憶體結構中最複雜的一部分.


一 , Library cache存放什麼(存放的資訊單元都叫做物件) ?  

Library存放的資訊單元都叫做物件,這些物件可以分為兩類: 

1. 儲存物件
2. 過渡物件(遊標Cursor,這裡的遊標指生成的可執行的物件, 執行相同SQL的多個程式可以共享該SQL產生的遊標,節省記憶體。)

A. 使用者提交的SQL
B. SQL語句相關的解析樹
C. 執行計劃
D. 使用者提交的PL/SQL程式塊(包括匿名程式塊,procedure,packages,function等)
E. PL/SQL物件依賴的table,index,view等物件
F. 控制結構:lock,pin,dependency table 等

備註: LIBRARY CACHE的物件可以在V$DB_OBJECT_CACHE中找到,這個檢視基於X$KGLOB。

 

 

二, SQL的解析及遊標

SQL在解析階段主要完成以下步驟 :


1.  將父遊標儲存到Library Cache中 (父遊標的概念參考後面的說明,這一步其實不包含
    在解析過程中)

     先將SQL轉化為ASCII數值,然後對這些ASCII數值進行hash函式的運算生成hash value    (10g還有唯一的SQL_ID),運算後匹配library cache裡的hash bucket (hash bucket簡單來    講是使用hash演算法將進入library cache中的SQL 透過一個類似二維陣列來表示,比如t[3][6],     每次查詢時透過hash演算法算出符合的bucket號,找到對應bucket,比如前面t[3][6]中的3號,    每個bucket後面會掛載所有滿足hash演算法的object handle, object handle會儲存SQL名稱    [對於SQL而言就是SQL文字], namespace等) ,再匹配hash bucket上面的handle,也就是控制程式碼,    如果匹配成功,那麼去找子游標 (子游標的概念參考後面的說明,找到子游標那麼直接執行,    如果子游標被交換出庫快取, 那麼透過父遊標資訊重新構造reload一個子遊標) , 如果不成功,    即不存在共享的父遊標,就會在庫快取中分配一些記憶體(Chunk),並將新產生的父遊標儲存進    庫快取,生成一個handle(物件控制程式碼),掛載hash bucket上。接下來進行硬解析。

2 . 包含VPD(虛擬專用資料庫)的約束條件
      虛擬專用資料庫VPD詳細資訊見後備注。比如對於HR工資的查詢,select salary from emp ;       如果設定VPD, 會隱含加入每個使用者各自的賬號,只能檢視自己的,句子會變成類似:       select salary from emp where name='susan' ;  

3.  對SQL語句進行文法檢查,如果存在文法錯誤,則退出解析過程  
      確認sql語句是否正確書寫(比如沒有寫from,select拼寫錯誤等),

4.  到資料字典校驗SQL涉及的物件和列是否存在,不存在就退出解析過程,這個過程會載入    Dictionary Cache .

5.  將物件進行名稱轉換,比如將synonym 轉換為實際的物件等。若轉換失敗則退出解析。

6.  檢查發出SQL語句的使用者(一般指連線使用者)是否有訪問SQL中引用的物件的許可權,若沒有則 退出解析。

7.  邏輯最佳化 -- 用一定的轉換技巧(Transforming Queries,查詢轉換器),生成語法語義上等同    的新的SQL語句。查詢語句的形式會影響所產生的執行計劃,查詢轉換器的作用就是改變查詢語    句的形式以產生較好的執行計劃。四種常見轉換技術:檢視合併(View Merging)、謂詞推進   (Predicate Pushing)、非巢狀子查詢(Subquery Unnesting)和物化檢視的查詢重寫(Query
    Rewrite with Materialized Views)。 

  詳細可以參考以下文件及後面備註 :  
  

8.  物理最佳化 -- 首先,生成與每個邏輯最佳化產生的sql語句有關的執行計劃, 接著, 根據    資料字典找到相關的統計資訊或者動態收集的統計資訊,計算出與執行計劃相關的開銷。最後,    選中最低開銷的執行計劃。涉及大量數學運算,所以這一步最消耗CPU資源。 子游標會在這一步    生成 ,執行計劃,繫結變數及執行環境是子游標中的主要內容。 

9.  將子游標load到庫快取 -- 首先分配記憶體(Chunk),然後將共享子游標儲存進去,最後將它與父遊標    關聯,與子游標有關的關鍵內容是執行計劃和執行環境,一旦儲存到庫快取,父遊標與子游標就可以 分別透過檢視v$sqlarea和v$sql被具體化。

v$sql中透過child_number,hash_value,address來確定一個子遊標,而v$sqlarea透過address和hash_value可以確定一個父遊標; 而從10g過後,透過sql_id就能確定一個遊標; 查詢是否有共享的父遊標
和硬解析是兩個不同的過程,父遊標共享與否和硬解析沒有直接關係,子游標的共享狀態決定軟硬解析 。

 

備註: 
---------------------------------------------------------------------------------- 

Namespace:
使用hash演算法對SQL語句對應的ASCII進行運算時,傳入函式的引數有SQL語句名稱及namespace(可透過v$librarycache查詢到各種不同的namespace,對於SQL而言值為"SQL AREA") .

VPD虛擬專用資料庫的詳細資訊:

SQL Parsing Flow Diagram [ID 32895.1] 


解析過程中的邏輯最佳化部分的查詢轉換器 --- 

從Oracle 8i開始就有四種轉換技術:檢視合併(View Merging)、謂詞推進(Predicate Pushing)、非巢狀子查詢(Subquery Unnesting)和物化檢視的查詢重寫(Query Rewrite with Materialized Views)。
 
檢視合併:如果SQL語句中含有檢視,經分析後會把檢視放在獨立的“檢視查詢塊”中,每個檢視會產生一個檢視子計劃,當為整個語句產生執行計劃時,檢視子計劃會被直接拿來使用而不會照顧到語句的整體性,這樣就很容易導致不良執行計劃的生成。檢視合併就是為了去掉“檢視查詢塊”,將檢視合併到一個整體的查詢塊中,這樣就不會有檢視子計劃產生,執行計劃的優良性得到提升。

謂詞推進:不是所有的檢視都能夠被合併,對於那些不能被合併的檢視Oracle會將相應的謂詞推進到檢視查詢塊中,這些謂詞通常是可索引的或者是過濾性較強的。

非巢狀子查詢:子查詢和檢視一樣也是被放於獨立查詢塊中的,查詢轉換器會將絕大多數子查詢轉換為連線從而合併為同一查詢塊,少量不能被轉換為連線的子查詢,會將它們的子計劃安照一個高效的方式排列。

物化檢視的查詢重寫:當query_rewrite_enabled=true時,查詢轉換器尋找與該查詢語句相關聯的物化檢視,並用物化檢視改寫該查詢語句。

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

 


三, 父遊標與子游標 


部分內容參考: 
http://www.oraclefans.cn/forum/showblog.jsp?rootid=5553 
http://www.itpub.net/thread-1362874-1-1.html  (問題)    

在硬解析的過程中,程式會一直持有library cache latch,直到硬解析結束。硬解析過程會為該SQL產生兩個遊標,一個是父遊標,另一個是子游標。

 

父遊標和子游標相關問題的討論:
http://www.itpub.net/thread-1362874-1-1.html 


父遊標(parent cursor) ---

當使用者A發出一條SQL後,Oracle會根據SQL文字內容生成hash value(10g還有唯一的SQL_ID),對比庫快取中的hash value, 以便能夠快速找到Shared pool中已經存在的相同SQL。如果找不到,則Oracle會為這個SQL建立一個parent cursor和一個child cursor。

父遊標裡主要包含兩種資訊:SQL文字以及最佳化目標(optimizer goal)。從v$sqlarea檢視中看到的都是有關父遊標的資訊,v$sqlarea中的每一行代表了一個parent cursor,  比如SQL文字對應欄位SQL_TEXT, 最佳化目標(optimizer goal)對應後面的RUNTIME_MEM,EXECUTIONS,CPU_TIME, DISK_READS, BUFFER_GETS 等等 。

父遊標在第一次開啟時被鎖定,直到其他所有的session都關閉遊標後才被解鎖。當父遊標被鎖定的時候它是不能被交換出library cache的,只有在解鎖以後才能被交換出library cache,這時該父遊標對應的所有子游標也被交換出library cache。

一個CURSOR的結構包括PARENT CURSOR和CHILD CURSOR,每個CURSOR至少包含一個CHILD CURSOR。這個CURSOR透過HASHVALUE來區別,每個PARENT CURSOR至少包含一個HEAP0,裡面存放環境、狀態和繫結變數的資訊。每個PARENT CURSOR至少有一個CHILD CURSOR 。handle其實就是存放的父遊標,真正的執行計劃是存放在子游標上的,也就是heap6上。

PARENT CURSOR是由一個handle和一個object組成,可以透過在庫快取hash table中的hash value查詢到handle, 而object 包含了指向它的每個 "child" cursor的指標 。

V$SQLAREA中version_count看到一個父遊標對應多少個子遊標,對應關係是靠hash_value及adress(SQL文字的地址)聯絡的,V$SQL中相同SQL文字的不同子游標,hash_value及adress是相同的,但是子地址child_address卻不一樣,這裡的子地址實際就是子游標所對應的Heap0的控制程式碼(handel)。 V$SQL中的hild_number編號從0開始,同樣SQL文字(父遊標共享)不同的child_number對應不同的child_address 。Oracle10g版本下V$SQL中有有3個欄位bind_data,
optimizer_env , optimizer_env_hash_value 應該是用於決定取哪個子游標的欄位。不過9i 中v$sql中沒有這些欄位,具體如何查詢到子游標的參考討論 :
http://www.itpub.net/thread-1362874-1-1.html  


子游標 (Child Cursors) ---

子游標包括遊標所有的資訊,比如具體的執行計劃、繫結變數等。子游標隨時可以被交換出library cache,當子游標被交換出library cache時,oracle可以利用父遊標的資訊重新構建出一個子遊標來,這個過程叫reload。 子游標具體的個數可以從v$sqlarea的version_count欄位體現出來。而每個具體的子游標則全都在v$sql裡體現。可以使用下面的方式來確定reload的比率:
SELECT 100*sum(reloads)/sum(pins) Reload_Ratio FROM v$librarycache;

一個父遊標可以對應多個子遊標。當具體的繫結變數的值與上次的繫結變數的值有較大差異(比如上次執行的繫結變數的值的長度是6位,而這次執行的繫結變數的值的長度是200 位)時或者當SQL語句完全相同,但是所引用的物件屬於不同的schema時,或執行SQL的環境不同(最佳化器模式不一樣), 都會建立一個新的子游標。


關於子游標新建和reload 的區別,如果所有版本的子游標都不能被共享,那麼會建立一個新的子游標 (new create) ,這種情況指的就是 environment 或bind var 長度不一樣等 導致的情況 ;而reload 指的是父遊標可以共享, 同樣的子游標 (environment 或bind var 等都一樣)原來已經存在於library cache, 因為某種原因被aged out出去,而現在需要它了,要重新裝載回來。


每個child cursor也是由一個handle和一個object構成. child object 又是由兩個heap即heap0及heap6 組成,其中Heap0包含所有的針對SQL語句每個版本的標示資訊(比如Environment, Statistics, Bind Variables等,比如繫結變數值不同的長度可能導致sql解析版本的不同; Child cursors are also called versions. ),Heap6包含執行計劃 。

Child cursor包含了SQL的metadata,也就是使這個SQL可以執行的所有相關資訊,如OBJECT和許可權,最佳化器設定,執行計劃等。v$sql中的每一行表示了一個child cursor,根據hash value和address與parent cursor 關聯。child cursor有自己的address,即V$SQL.CHILD_ADDRESS。如果有多個child cursor,則表示parent cursor有多個版本,v$sqlarea中的version_count欄位就會紀錄下來。

 

每種型別的dml語句都需要如下階段:

Create a Cursor         建立遊標
Parse the Statement     解析語句
Bind Any Variables      繫結變數
Run the Statement       執行語句
Close the Cursor        關閉遊標

 

 


四,  硬解析與軟解析,"軟軟"解析,RELOAD


硬解析 --- 
首先了解父遊標共享的條件 :

1. 字元級的比較, 要求文字完全一致
   SQL語句必須完全相同,select * from emp; 和select     *  from emp; 是不一樣的。不能共享。

2. 必須使用相同名稱的繫結變數(其實就是文字字元不一致),比如
   select age from pepoo where name=:var_p
   select age from pepoo where name=:var_f
   (即使在執行的時候賦予這兩個不同名稱的繫結變數一樣的值,也不能通向父遊標)

從SQL解析過程可以看出,父遊標是否共享是發生在硬解析之前,所以父遊標是否能共享和硬解析沒有關係,不過父遊標不能共享則一定是硬解析,硬解析的整個過程見上面的第二節 。但是父遊標共享了不一定就是軟解析。能否避免硬解析,還要看子游標 。

 

--------------------------------------------------------- 
父遊標共享已經討論過,這裡討論子游標共享的幾種情況 (假設CURSOR_SHARING=EXACT ):   

第一種是A發出的原始SQL語句和其他使用者B之前發出的SQL文字一模一樣,父親遊標可以共享,但是因為最佳化器環境設定不同( OPTIMIZER_MISMATCH),  繫結變數的值的長度在第二次執行的時候發生顯著的變化(BIND_MISMATCH) , 授權關係不匹配(AUTH_CHECK_MISMATCH ) 或者 基礎物件轉換不匹配(TRANSLATION_MISMATCH) 等導致子游標不能共享,需要新生成一個子遊標 。 這與SQL共享(即遊標共享)是有關係的 。 這種情況下的執行計劃可能不同,也可能相同(我們可以透過plan_hash_value看出);  
這裡因為除SQL TEXT之外的其他條件不符合,所以reload 也不會發生 。子游標就是new create and load,應該是硬解析 。具體的mismatch可以查詢 V$SQL_SHARED_CURSOR .   ;

例如:

--視窗1執行

sys/SYS>alter session set optimizer_mode=all_rows;
Session altered.

sys/SYS>select * from tt;
no rows selected

sys/SYS>alter session set optimizer_mode=first_rows_10;
Session altered.

sys/SYS>select * from tt;
no rows selected


--視窗2執行

sys/SYS>select hash_value,sql_text,executions,VERSION_COUNT from
        v$sqlarea where sql_text like '%from tt';

HASH_VALUE SQL_TEXT                                 EXECUTIONS VERSION_COUNT
---------- ---------------------------------------- ---------- -------------
3762890390 select * from tt                                  2             2

sys/SYS>select HASH_VALUE,child_number,sql_text from v$sql where sql_text like '%from tt';

HASH_VALUE CHILD_NUMBER SQL_TEXT
---------- ------------ ----------------------------------------
3762890390            0 select * from tt
3762890390            1 select * from tt

可以看到,SQL文字是完全相同的,所以兩個子游標共享了一個父遊標。但是由於optimizer_mode的不同,所以生成了2個子遊標。如果產生了子游標,那麼說明肯定產生了某種mismatch,如何來檢視是何種原因產生了mismatch,要透過v$sql_shared_cursor。

sys/SYS>select kglhdpar, address,auth_check_mismatch, translation_mismatch,OPTIMIZER_MISMATCH
  2  from v$sql_shared_cursor
  3  where kglhdpar in
  4  ( select address
  5    from v$sql
  6    where sql_text like '%from tt');

KGLHDPAR ADDRESS  A T O
-------- -------- - - -
89BB8948 89BB83CC N N N
89BB8948 89BB5E78 N N Y   

可以看到OPTIMIZER_MISMATCH列第二行的值為Y,這說明了正是由於optimizer_mode的不同而產生了子游標。最後,父遊標和子游標的意義何在?其實一切都是為了共享。以減少再次解析的資源浪費。


第二種是A發出的原始SQL語句和與在shared pool 中的SQL文字一模一樣,父遊標可以共享,子游標不存在所謂的mismatch ,  目前也存在於庫快取中,可以共享子游標,那麼應該是軟解析 。

第三種,父遊標可以共享, 不同的是,子游標本來是可以共享的,但是目前被交換出(aged out)庫快取,這時會reload 子游標,也就是利用父遊標的資訊重新構造出一個子遊標 ,Oracle已經知道應該共享哪個子游標,只是它暫時被交換出庫快取, reload應該不屬於硬解析,是否屬於軟解析呢 ?雖然被aged out 出庫快取,但是可能某個地方會記錄這個子游標的一些資訊,而不需要重新生成子游標的相關資訊(比如執行計劃等),  而只需要reload  (reload的具體過程是什麼還需要研究)  。

查詢是否有共享的父遊標和硬解析是兩個不同的過程,父遊標共享與否和硬解析沒有直接關係, 子游標的共享狀態決定軟硬解析 。

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


從效能的角度來看,儘量避免硬解析,為什麼?
第一: 因為邏輯最佳化(Transforming Queries)和物理最佳化(選擇最優執行計劃)都非常依賴CPU的操作。
第二: 需要分配記憶體來將父遊標與子游標儲存到庫快取中。由於庫快取是在所有的會話之間共享,
庫快取中的記憶體分配必須是序列執行。

 


軟解析,"軟軟"解析 --- 

軟解析是相對於硬解析而言的,其實只要在hash bucket裡可以匹配對應的SQL文字(算一次get),那麼就是軟解析,說明之前執行過該sql,其實sql執行期間只要一個或多個步驟可以跳過,那麼我們就可以定位為軟解析。如果這個SQ語句沒有被找到,就進行硬解析。軟解析有三種型別:

A.  第一種是某session發出的SQL語句與在library cache裡其他session發出的SQL一致,父遊標和子游標都可以共享,邏輯最佳化(Transforming Queries),和物理最佳化(選擇最優執行計劃)及將這些資訊裝載到庫快取的heap中 這幾個步驟可以省略,表名,列名,名稱轉化及許可權檢查還是需要的。

B.  第二種是某session發出的SQL是該session之前發出的曾經執行過的SQL。這時,解析過程只需要進行文法檢查及許可權檢查。

C.  第三種是當設定了session_cached_cursors時,當某個session第三次執行相同的SQL時,則會把該SQL的遊標資訊轉移到該session的PGA中。這樣,該session以後再執行相同的SQL語句時,會直接從PGA裡取出執行計劃,跳過硬解析的所有步驟,這是最高效的解析方式,但是會消耗很大的記憶體。俗稱為"軟軟"解析 。

 

 

Reload --- 

關於子游標新建和reload 的區別,如果各版本的子游標都不能被共享,那麼會建立一個新的子游標 (new create) ,這種情況指的就是 environment 或繫結變數長度不一樣等 導致的情況。 而reload 指的是父遊標可以共享,同樣的子游標 (執行計劃,environment 或bind var 等都一樣)原來已經存在於library cache, 因為某種原因被aged out出去,而現在需要它了,要重新裝載回來 (Oracle資料庫可能在某個地方儲存了原來相同的子游標資訊)。

在Hash bucket中查詢SQL,如果有的話就算作是一次get,並查詢這個SQL語句的執行計劃,如果執行計劃已經不存在了(age out)或者是存在但不可用(Invalidation),那麼就必須對這條sql語句重新裝載,這就叫reload,如果執行計劃存在並且可用的話,oracle就執行這句話,這就叫做execution

 

 

五, 繫結變數(Bind Variables)


優點: 共享遊標,減少硬解析

繫結變數分級 --

前面說到執行環境的變化比如繫結變數定義的型別大小不同會導致生成不同的遊標,為了使遊標的數量不至於太多,產生了這個功能。此功能將變數的長度分為4個級別,0-32位元組,33-128位元組,129-2000位元組,>2000位元組 這四個等級。不用說,同一個繫結變數(長度)的變化,最多能生成4個遊標。

缺點:  繫結變數也有缺點。缺點就是,相對於字面量而言,會減弱查詢最佳化器的功能。

比如:
select count(*) from t where id > 10;
select count(*) from t where id > 99999;
根據id值10,99999和表的統計資訊,查詢最佳化器可能會選擇全表掃描或者索引掃描,是合理的。

使用了繫結變數,最佳化器會忽略他們的具體值,從而生成相同的執行計劃。為了解決這個問題,
oracle9i引入了繫結變數窺測(bind variable peeking)的功能。

繫結變數窺測的優點,就是窺測繫結變數的值,把它們當做字面量來使用。這樣的好處,就是能獲得最優查詢路徑,比如是選擇全表掃描還是索引掃描。

繫結變數窺測也有缺點,即生成的執行計劃依賴第一次生成執行計劃時所提供的值。舉例來說,就是如果第一次是全掃描,以後永遠都是全表掃描了。這個方法對於非OLTP系統的缺點非常明顯,因為一個繫結變數集可能返回的結果集只包含幾百行的資料,而另一套繫結變數可能返回幾百萬行資料,因此,Oracle建議保留CURSOR_SHARING作為該初始化引數的預設值,以強制產生一個新的更有效的執行計劃 (cursor_sharing的詳細解釋見後面)。

那麼如何避免這個缺點呢?只有升級到oracle11g了。
oracle11g引用一個新功能,自適應遊標共享(ACS)。這個功能就是根據繫結變數的值,可以為相同的sql語句,生成不同子游標,及不同的執行計劃。ACS使用了兩個新的度量機制:繫結敏感度和繫結感知。具體可以參考Oracle11g文件。


什麼時候不使用繫結變數?
批次任務處理,報表生成,運用OLAP的資料倉儲,因為這種大型的查詢時間較長,一次新的硬解析相對於這個查詢時間不算什麼, 所以不用繫結變數沒有什麼影響 。如果使用繫結變數,10g或以前的版本,一旦第一次執行時繫結變數第一次提供的值如果是小範圍的,那麼可能是索引掃描,但是第二次可能是資料倉儲典型的大時間範圍的查詢,需要全表掃描,但是還是沿用了前面的索引掃描,這樣導致效能下降。OLTP型別大多數是小量密集的操作,所以使用繫結變數時相對最優的執行計劃比較穩定 。 
 
在我們不使用where等條件判斷時我們就要儘量使用繫結變數(比如普通insert操作),沒理由不使用繫結變數;   而涉及到基數選擇性判斷時我們應該儘量避免使用繫結變數,因為在物理最佳化階段的繫結變數窺測遇到較大負面風險。

也可以參考下面的兩種建議:

如果sql處理的資料較少, 解析時間顯然比執行時間多很多了,那麼我們應該儘量使用繫結變數,這種適用於 OLTP(聯機事務處理系統);
而如果是資料倉儲型別的資料庫,我們對繫結變數的使用就應該慎重了,因為這時的執行時間有可能遠遠大於解析時間,解析時間相對於執行時間近乎可以忽略,所以這時應該儘量不使用繫結變數。

 


引數CURSOR_SHARING ---

oracle是為了滿足一些以前開發的程式,裡面有大量的相似的statement,沒有很好的使用繫結變數,但是重寫有不現實的情況下使用的一個引數。並且oracle也不建議修改這個引數。保持預設即可。

語法 CURSOR_SHARING = { SIMILAR | EXACT | FORCE } ,預設值為 EXACT


EXACT --
僅僅允許絕對一樣的SQL語句共享同樣的遊標。當一個SQL語句解析的時候,首先到shared pool區檢視是否有完全一樣的語句存在,如果不存在(其實此時是找不到共享的父遊標),就執行hard parse .

SIMILAR --
如果在shared pool中無法找到完全一樣的語句的時候,就會在shared pool進行一次新的查詢,就是查詢和當前要解析的語句相似的SQL語句。 similar語句就是除了value of some literal不同,別的地方都相同的語句。比如下面:
select * from a where age=2;
select * from a where age=5;
如果在shared pool中查詢到這樣的語句,就會做下一步的檢查,看shared pool中快取的這個語句的execution plan是否適合當前解析的語句,如果適合,就會使用shared pool的語句,而不去做hard parse。

FORCE --
強制將字面值不一樣的但是其他方面是一樣的SQL語句共享遊標。如果cursor_sharing設定為force的時候,當在shared pool中發現了similar statement之後,就不會再去檢查執行計劃了,而直接使用在shared pool中的這個語句了。

將cursor_sharing設定為force實際上是危險的。這會可能形成suboptimal的執行計劃。比如對於一個範圍查詢的語句,比如select * from a where a>10 and a<20這樣型別的語句,快取中的語句的執行計劃可能對於正在解析的語句就是不適合的,不是最優的執行計劃。這樣看起來是減少了解析的時間,但是大大增大了execution的時間。

什麼時候需要修改這個引數呢?需要滿足以下的條件:
一個是由於大量的shared pool hitmis影響了使用者的響應時間(就是當前的shared pool無法滿足共享sql語句儲存的需要),如果沒有這個問題,那麼設定這個引數,可能會造成更糟糕的效能。這個引數僅僅只是減少parse的時間。另外一個就是在現有程式中有大量的similar statement,可透過設定這個引數來獲得相對比較好的效能。

 

---------------------------------------------------------------
關於cursor_sharing = similar的測試 :

若存在object_id的 histograms ,則每次是不同的 值 的時候都產生硬解析 ,若不存在 histograms ,則不產生硬解析 。換句話說,當表的欄位被分析過存在histograms的時候,similar 的表現和exact一樣,當表的欄位沒被分析不存在histograms的時候,similar的表現和force一樣。這樣避免了一味地如force一樣轉換成變數形式,因為有hostograms的情況下轉換成變數之後就容易產生錯誤的執行計劃,沒有利用上統計資訊。而exact呢,在沒有histograms的情況下也要分別產生硬解析,這樣的話,由於執行計劃不會受到資料分佈的影響(因為沒有統計資訊)重新解析是沒有實質意義的。而similar則綜合了兩者的優點。

備註: cursor_sharing=force  or similar時,在9205以下的版本BUG不少 。 
---------------------------------------------------------------

 

Library cache內部機制詳解    參考:

 

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

相關文章