一 , Library cache存放什么(存放的信息單元都叫做對(duì)象) ?
Library存放的信息單元都叫做對(duì)象,這些對(duì)象可以分為兩類:
1. 存儲(chǔ)對(duì)象
2. 過渡對(duì)象(游標(biāo)Cursor,這里的游標(biāo)指生成的可執(zhí)行的對(duì)象, 運(yùn)行相同SQL的多個(gè)進(jìn)程可以共享該SQL產(chǎn)生的游標(biāo),節(jié)省內(nèi)存。)
A. 用戶提交的SQL
B. SQL語句相關(guān)的解析樹
C. 執(zhí)行計(jì)劃
D. 用戶提交的PL/SQL程序塊(包括匿名程序塊,procedure,packages,function等)
E. PL/SQL對(duì)象依賴的table,index,view等對(duì)象
F. 控制結(jié)構(gòu):lock,pin,dependency table 等
備注: LIBRARY CACHE的對(duì)象可以在V$DB_OBJECT_CACHE中找到,這個(gè)視圖基于X$KGLOB。
二, SQL的解析及游標(biāo)
SQL在解析階段主要完成以下步驟 :
1. 將父游標(biāo)保存到Library Cache中 (父游標(biāo)的概念參考后面的說明,這一步其實(shí)不包含
在解析過程中)
先將SQL轉(zhuǎn)化為ASCII數(shù)值,然后對(duì)這些ASCII數(shù)值進(jìn)行hash函數(shù)的運(yùn)算生成hash value (10g還有唯一的SQL_ID),運(yùn)算后匹配library cache里的hash bucket (hash bucket簡單來 講是使用hash算法將進(jìn)入library cache中的SQL 通過一個(gè)類似二維數(shù)組來表示,比如t[3][6], 每次查找時(shí)通過hash算法算出符合的bucket號(hào),找到對(duì)應(yīng)bucket,比如前面t[3][6]中的3號(hào), 每個(gè)bucket后面會(huì)掛載所有滿足hash算法的object handle, object handle會(huì)存儲(chǔ)SQL名稱 [對(duì)于SQL而言就是SQL文本], namespace等) ,再匹配hash bucket上面的handle,也就是句柄, 如果匹配成功,那么去找子游標(biāo) (子游標(biāo)的概念參考后面的說明,找到子游標(biāo)那么直接執(zhí)行, 如果子游標(biāo)被交換出庫緩存, 那么通過父游標(biāo)信息重新構(gòu)造reload一個(gè)子游標(biāo)) , 如果不成功, 即不存在共享的父游標(biāo),就會(huì)在庫緩存中分配一些內(nèi)存(Chunk),并將新產(chǎn)生的父游標(biāo)保存進(jìn) 庫緩存,生成一個(gè)handle(對(duì)象句柄),掛載hash bucket上。接下來進(jìn)行硬解析。
2 . 包含VPD(虛擬專用數(shù)據(jù)庫)的約束條件
虛擬專用數(shù)據(jù)庫VPD詳細(xì)信息見后備注。比如對(duì)于HR工資的查詢,select salary from emp ; 如果設(shè)置VPD, 會(huì)隱含加入每個(gè)用戶各自的賬號(hào),只能查看自己的,句子會(huì)變成類似: select salary from emp where name=\\\’susan\\\’ ;
3. 對(duì)SQL語句進(jìn)行文法檢查,如果存在文法錯(cuò)誤,則退出解析過程
確認(rèn)sql語句是否正確書寫(比如沒有寫from,select拼寫錯(cuò)誤等),
4. 到數(shù)據(jù)字典校驗(yàn)SQL涉及的對(duì)象和列是否存在,不存在就退出解析過程,這個(gè)過程會(huì)加載 Dictionary Cache .
5. 將對(duì)象進(jìn)行名稱轉(zhuǎn)換,比如將synonym 轉(zhuǎn)換為實(shí)際的對(duì)象等。若轉(zhuǎn)換失敗則退出解析。
6. 檢查發(fā)出SQL語句的用戶(一般指連接用戶)是否有訪問SQL中引用的對(duì)象的權(quán)限,若沒有則 退出解析。
7. 邏輯優(yōu)化 — 用一定的轉(zhuǎn)換技巧(Transforming Queries,查詢轉(zhuǎn)換器),生成語法語義上等同 的新的SQL語句。查詢語句的形式會(huì)影響所產(chǎn)生的執(zhí)行計(jì)劃,查詢轉(zhuǎn)換器的作用就是改變查詢語 句的形式以產(chǎn)生較好的執(zhí)行計(jì)劃。四種常見轉(zhuǎn)換技術(shù):視圖合并(View Merging)、謂詞推進(jìn) (Predicate Pushing)、非嵌套子查詢(Subquery Unnesting)和物化視圖的查詢重寫(Query
Rewrite with Materialized Views)。
詳細(xì)可以參考以下文檔及后面?zhèn)渥?:
http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/optimops.htm#i37745
8. 物理優(yōu)化 — 首先,生成與每個(gè)邏輯優(yōu)化產(chǎn)生的sql語句有關(guān)的執(zhí)行計(jì)劃, 接著, 根據(jù) 數(shù)據(jù)字典找到相關(guān)的統(tǒng)計(jì)信息或者動(dòng)態(tài)收集的統(tǒng)計(jì)信息,計(jì)算出與執(zhí)行計(jì)劃相關(guān)的開銷。最后, 選中最低開銷的執(zhí)行計(jì)劃。涉及大量數(shù)學(xué)運(yùn)算,所以這一步最消耗CPU資源。 子游標(biāo)會(huì)在這一步 生成 ,執(zhí)行計(jì)劃,綁定變量及執(zhí)行環(huán)境是子游標(biāo)中的主要內(nèi)容。
9. 將子游標(biāo)load到庫緩存 — 首先分配內(nèi)存(Chunk),然后將共享子游標(biāo)存儲(chǔ)進(jìn)去,最后將它與父游標(biāo) 關(guān)聯(lián),與子游標(biāo)有關(guān)的關(guān)鍵內(nèi)容是執(zhí)行計(jì)劃和執(zhí)行環(huán)境,一旦保存到庫緩存,父游標(biāo)與子游標(biāo)就可以 分別通過視圖v$sqlarea和v$sql被具體化。
v$sql中通過child_number,hash_value,address來確定一個(gè)子游標(biāo),而v$sqlarea通過address和hash_value可以確定一個(gè)父游標(biāo); 而從10g過后,通過sql_id就能確定一個(gè)游標(biāo); 查找是否有共享的父游標(biāo)
和硬解析是兩個(gè)不同的過程,父游標(biāo)共享與否和硬解析沒有直接關(guān)系,子游標(biāo)的共享狀態(tài)決定軟硬解析 。
備注:
———————————————————————————-
Namespace:
使用hash算法對(duì)SQL語句對(duì)應(yīng)的ASCII進(jìn)行運(yùn)算時(shí),傳入函數(shù)的參數(shù)有SQL語句名稱及namespace(可通過v$librarycache查詢到各種不同的namespace,對(duì)于SQL而言值為SQL AREA) .
VPD虛擬專用數(shù)據(jù)庫的詳細(xì)信息:
http://www.oracle.com/technology/global/cn/pub/articles/10gdba/week14_10gdba.html
SQL Parsing Flow Diagram [ID 32895.1]
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&id=32895.1
解析過程中的邏輯優(yōu)化部分的查詢轉(zhuǎn)換器 —
從Oracle 8i開始就有四種轉(zhuǎn)換技術(shù):視圖合并(View Merging)、謂詞推進(jìn)(Predicate Pushing)、非嵌套子查詢(Subquery Unnesting)和物化視圖的查詢重寫(Query Rewrite with Materialized Views)。
視圖合并:如果SQL語句中含有視圖,經(jīng)分析后會(huì)把視圖放在獨(dú)立的“視圖查詢塊”中,每個(gè)視圖會(huì)產(chǎn)生一個(gè)視圖子計(jì)劃,當(dāng)為整個(gè)語句產(chǎn)生執(zhí)行計(jì)劃時(shí),視圖子計(jì)劃會(huì)被直接拿來使用而不會(huì)照顧到語句的整體性,這樣就很容易導(dǎo)致不良執(zhí)行計(jì)劃的生成。視圖合并就是為了去掉“視圖查詢塊”,將視圖合并到一個(gè)整體的查詢塊中,這樣就不會(huì)有視圖子計(jì)劃產(chǎn)生,執(zhí)行計(jì)劃的優(yōu)良性得到提升。
謂詞推進(jìn):不是所有的視圖都能夠被合并,對(duì)于那些不能被合并的視圖Oracle會(huì)將相應(yīng)的謂詞推進(jìn)到視圖查詢塊中,這些謂詞通常是可索引的或者是過濾性較強(qiáng)的。
非嵌套子查詢:子查詢和視圖一樣也是被放于獨(dú)立查詢塊中的,查詢轉(zhuǎn)換器會(huì)將絕大多數(shù)子查詢轉(zhuǎn)換為連接從而合并為同一查詢塊,少量不能被轉(zhuǎn)換為連接的子查詢,會(huì)將它們的子計(jì)劃安照一個(gè)高效的方式排列。
物化視圖的查詢重寫:當(dāng)query_rewrite_enabled=true時(shí),查詢轉(zhuǎn)換器尋找與該查詢語句相關(guān)聯(lián)的物化視圖,并用物化視圖改寫該查詢語句。
———————————————————————————-
三, 父游標(biāo)與子游標(biāo)
部分內(nèi)容參考:
http://www.oraclefans.cn/forum/showblog.jsp?rootid=5553
http://www.itpub.net/thread-1362874-1-1.html (問題)
在硬解析的過程中,進(jìn)程會(huì)一直持有l(wèi)ibrary cache latch,直到硬解析結(jié)束。硬解析過程會(huì)為該SQL產(chǎn)生兩個(gè)游標(biāo),一個(gè)是父游標(biāo),另一個(gè)是子游標(biāo)。
父游標(biāo)和子游標(biāo)相關(guān)問題的討論:
http://www.itpub.net/thread-1362874-1-1.html
父游標(biāo)(parent cursor) —
當(dāng)用戶A發(fā)出一條SQL后,Oracle會(huì)根據(jù)SQL文本內(nèi)容生成hash value(10g還有唯一的SQL_ID),對(duì)比庫緩存中的hash value, 以便能夠快速找到Shared pool中已經(jīng)存在的相同SQL。如果找不到,則Oracle會(huì)為這個(gè)SQL創(chuàng)建一個(gè)parent cursor和一個(gè)child cursor。
父游標(biāo)里主要包含兩種信息:SQL文本以及優(yōu)化目標(biāo)(optimizer goal)。從v$sqlarea視圖中看到的都是有關(guān)父游標(biāo)的信息,v$sqlarea中的每一行代表了一個(gè)parent cursor, 比如SQL文本對(duì)應(yīng)字段SQL_TEXT, 優(yōu)化目標(biāo)(optimizer goal)對(duì)應(yīng)后面的RUNTIME_MEM,EXECUTIONS,CPU_TIME, DISK_READS, BUFFER_GETS 等等 。
父游標(biāo)在第一次打開時(shí)被鎖定,直到其他所有的session都關(guān)閉游標(biāo)后才被解鎖。當(dāng)父游標(biāo)被鎖定的時(shí)候它是不能被交換出library cache的,只有在解鎖以后才能被交換出library cache,這時(shí)該父游標(biāo)對(duì)應(yīng)的所有子游標(biāo)也被交換出library cache。
一個(gè)CURSOR的結(jié)構(gòu)包括PARENT CURSOR和CHILD CURSOR,每個(gè)CURSOR至少包含一個(gè)CHILD CURSOR。這個(gè)CURSOR通過HASHVALUE來區(qū)別,每個(gè)PARENT CURSOR至少包含一個(gè)HEAP0,里面存放環(huán)境、狀態(tài)和綁定變量的信息。每個(gè)PARENT CURSOR至少有一個(gè)CHILD CURSOR 。handle其實(shí)就是存放的父游標(biāo),真正的執(zhí)行計(jì)劃是存放在子游標(biāo)上的,也就是heap6上。
PARENT CURSOR是由一個(gè)handle和一個(gè)object組成,可以通過在庫緩存hash table中的hash value查找到handle, 而object 包含了指向它的每個(gè) child cursor的指針 。
V$SQLAREA中version_count看到一個(gè)父游標(biāo)對(duì)應(yīng)多少個(gè)子游標(biāo),對(duì)應(yīng)關(guān)系是靠hash_value及adress(SQL文本的地址)聯(lián)系的,V$SQL中相同SQL文本的不同子游標(biāo),hash_value及adress是相同的,但是子地址child_address卻不一樣,這里的子地址實(shí)際就是子游標(biāo)所對(duì)應(yīng)的Heap0的句柄(handel)。 V$SQL中的hild_number編號(hào)從0開始,同樣SQL文本(父游標(biāo)共享)不同的child_number對(duì)應(yīng)不同的child_address 。Oracle10g版本下V$SQL中有有3個(gè)字段bind_data,
optimizer_env , optimizer_env_hash_value 應(yīng)該是用于決定取哪個(gè)子游標(biāo)的字段。不過9i 中v$sql中沒有這些字段,具體如何查找到子游標(biāo)的參考討論 :
http://www.itpub.net/thread-1362874-1-1.html
子游標(biāo) (Child Cursors) —
子游標(biāo)包括游標(biāo)所有的信息,比如具體的執(zhí)行計(jì)劃、綁定變量等。子游標(biāo)隨時(shí)可以被交換出library cache,當(dāng)子游標(biāo)被交換出library cache時(shí),oracle可以利用父游標(biāo)的信息重新構(gòu)建出一個(gè)子游標(biāo)來,這個(gè)過程叫reload。 子游標(biāo)具體的個(gè)數(shù)可以從v$sqlarea的version_count字段體現(xiàn)出來。而每個(gè)具體的子游標(biāo)則全都在v$sql里體現(xiàn)。可以使用下面的方式來確定reload的比率:
SELECT 100*sum(reloads)/sum(pins) Reload_Ratio FROM v$librarycache;
一個(gè)父游標(biāo)可以對(duì)應(yīng)多個(gè)子游標(biāo)。當(dāng)具體的綁定變量的值與上次的綁定變量的值有較大差異(比如上次執(zhí)行的綁定變量的值的長度是6位,而這次執(zhí)行的綁定變量的值的長度是200 位)時(shí)或者當(dāng)SQL語句完全相同,但是所引用的對(duì)象屬于不同的schema時(shí),或執(zhí)行SQL的環(huán)境不同(優(yōu)化器模式不一樣), 都會(huì)創(chuàng)建一個(gè)新的子游標(biāo)。
關(guān)于子游標(biāo)新建和reload 的區(qū)別,如果所有版本的子游標(biāo)都不能被共享,那么會(huì)創(chuàng)建一個(gè)新的子游標(biāo) (new create) ,這種情況指的就是 environment 或bind var 長度不一樣等 導(dǎo)致的情況 ;而reload 指的是父游標(biāo)可以共享, 同樣的子游標(biāo) (environment 或bind var 等都一樣)原來已經(jīng)存在于library cache, 因?yàn)槟撤N原因被aged out出去,而現(xiàn)在需要它了,要重新裝載回來。
每個(gè)child cursor也是由一個(gè)handle和一個(gè)object構(gòu)成. child object 又是由兩個(gè)heap即heap0及heap6 組成,其中Heap0包含所有的針對(duì)SQL語句每個(gè)版本的標(biāo)示信息(比如Environment, Statistics, Bind Variables等,比如綁定變量值不同的長度可能導(dǎo)致sql解析版本的不同; Child cursors are also called versions. ),Heap6包含執(zhí)行計(jì)劃 。
Child cursor包含了SQL的metadata,也就是使這個(gè)SQL可以執(zhí)行的所有相關(guān)信息,如OBJECT和權(quán)限,優(yōu)化器設(shè)置,執(zhí)行計(jì)劃等。v$sql中的每一行表示了一個(gè)child cursor,根據(jù)hash value和address與parent cursor 關(guān)聯(lián)。child cursor有自己的address,即V$SQL.CHILD_ADDRESS。如果有多個(gè)child cursor,則表示parent cursor有多個(gè)版本,v$sqlarea中的version_count字段就會(huì)紀(jì)錄下來。
每種類型的dml語句都需要如下階段:
Create a Cursor 創(chuàng)建游標(biāo)
Parse the Statement 解析語句
Bind Any Variables 綁定變量
Run the Statement 運(yùn)行語句
Close the Cursor 關(guān)閉游標(biāo)
四, 硬解析與軟解析,軟軟解析,RELOAD
硬解析 —
首先了解父游標(biāo)共享的條件 :
1. 字符級(jí)的比較, 要求文本完全一致
SQL語句必須完全相同,select * from emp; 和select * from emp; 是不一樣的。不能共享。
2. 必須使用相同名稱的綁定變量(其實(shí)就是文本字符不一致),比如
select age from pepoo where name=:var_p
select age from pepoo where name=:var_f
(即使在運(yùn)行的時(shí)候賦予這兩個(gè)不同名稱的綁定變量一樣的值,也不能通向父游標(biāo))
從SQL解析過程可以看出,父游標(biāo)是否共享是發(fā)生在硬解析之前,所以父游標(biāo)是否能共享和硬解析沒有關(guān)系,不過父游標(biāo)不能共享則一定是硬解析,硬解析的整個(gè)過程見上面的第二節(jié) 。但是父游標(biāo)共享了不一定就是軟解析。能否避免硬解析,還要看子游標(biāo) 。
———————————————————
父游標(biāo)共享已經(jīng)討論過,這里討論子游標(biāo)共享的幾種情況 (假設(shè)CURSOR_SHARING=EXACT ):
第一種是A發(fā)出的原始SQL語句和其他用戶B之前發(fā)出的SQL文本一模一樣,父親游標(biāo)可以共享,但是因?yàn)閮?yōu)化器環(huán)境設(shè)置不同( OPTIMIZER_MISMATCH), 綁定變量的值的長度在第二次執(zhí)行的時(shí)候發(fā)生顯著的變化(BIND_MISMATCH) , 授權(quán)關(guān)系不匹配(AUTH_CHECK_MISMATCH ) 或者 基礎(chǔ)物件轉(zhuǎn)換不匹配(TRANSLATION_MISMATCH) 等導(dǎo)致子游標(biāo)不能共享,需要新生成一個(gè)子游標(biāo) 。 這與SQL共享(即游標(biāo)共享)是有關(guān)系的 。 這種情況下的執(zhí)行計(jì)劃可能不同,也可能相同(我們可以通過plan_hash_value看出);
這里因?yàn)槌齋QL TEXT之外的其他條件不符合,所以reload 也不會(huì)發(fā)生 。子游標(biāo)就是new create and load,應(yīng)該是硬解析 。具體的mismatch可以查詢 V$SQL_SHARED_CURSOR . ;
例如:
–窗口1執(zhí)行
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執(zhí)行
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文本是完全相同的,所以兩個(gè)子游標(biāo)共享了一個(gè)父游標(biāo)。但是由于optimizer_mode的不同,所以生成了2個(gè)子游標(biāo)。如果產(chǎn)生了子游標(biāo),那么說明肯定產(chǎn)生了某種mismatch,如何來查看是何種原因產(chǎn)生了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的不同而產(chǎn)生了子游標(biāo)。最后,父游標(biāo)和子游標(biāo)的意義何在?其實(shí)一切都是為了共享。以減少再次解析的資源浪費(fèi)。
第二種是A發(fā)出的原始SQL語句和與在shared pool 中的SQL文本一模一樣,父游標(biāo)可以共享,子游標(biāo)不存在所謂的mismatch , 目前也存在于庫緩存中,可以共享子游標(biāo),那么應(yīng)該是軟解析 。
第三種,父游標(biāo)可以共享, 不同的是,子游標(biāo)本來是可以共享的,但是目前被交換出(aged out)庫緩存,這時(shí)會(huì)reload 子游標(biāo),也就是利用父游標(biāo)的信息重新構(gòu)造出一個(gè)子游標(biāo) ,Oracle已經(jīng)知道應(yīng)該共享哪個(gè)子游標(biāo),只是它暫時(shí)被交換出庫緩存, reload應(yīng)該不屬于硬解析,是否屬于軟解析呢 ?雖然被aged out 出庫緩存,但是可能某個(gè)地方會(huì)記錄這個(gè)子游標(biāo)的一些信息,而不需要重新生成子游標(biāo)的相關(guān)信息(比如執(zhí)行計(jì)劃等), 而只需要reload (reload的具體過程是什么還需要研究) 。
查找是否有共享的父游標(biāo)和硬解析是兩個(gè)不同的過程,父游標(biāo)共享與否和硬解析沒有直接關(guān)系, 子游標(biāo)的共享狀態(tài)決定軟硬解析 。
———————————————————
從性能的角度來看,盡量避免硬解析,為什么?
第一: 因?yàn)檫壿媰?yōu)化(Transforming Queries)和物理優(yōu)化(選擇最優(yōu)執(zhí)行計(jì)劃)都非常依賴CPU的操作。
第二: 需要分配內(nèi)存來將父游標(biāo)與子游標(biāo)保存到庫緩存中。由于庫緩存是在所有的會(huì)話之間共享,
庫緩存中的內(nèi)存分配必須是串行執(zhí)行。
軟解析,軟軟解析 —
軟解析是相對(duì)于硬解析而言的,其實(shí)只要在hash bucket里可以匹配對(duì)應(yīng)的SQL文本(算一次get),那么就是軟解析,說明之前運(yùn)行過該sql,其實(shí)sql執(zhí)行期間只要一個(gè)或多個(gè)步驟可以跳過,那么我們就可以定位為軟解析。如果這個(gè)SQ語句沒有被找到,就進(jìn)行硬解析。軟解析有三種類型:
A. 第一種是某session發(fā)出的SQL語句與在library cache里其他session發(fā)出的SQL一致,父游標(biāo)和子游標(biāo)都可以共享,邏輯優(yōu)化(Transforming Queries),和物理優(yōu)化(選擇最優(yōu)執(zhí)行計(jì)劃)及將這些信息裝載到庫緩存的heap中 這幾個(gè)步驟可以省略,表名,列名,名稱轉(zhuǎn)化及權(quán)限檢查還是需要的。
B. 第二種是某session發(fā)出的SQL是該session之前發(fā)出的曾經(jīng)執(zhí)行過的SQL。這時(shí),解析過程只需要進(jìn)行文法檢查及權(quán)限檢查。
C. 第三種是當(dāng)設(shè)置了session_cached_cursors時(shí),當(dāng)某個(gè)session第三次執(zhí)行相同的SQL時(shí),則會(huì)把該SQL的游標(biāo)信息轉(zhuǎn)移到該session的PGA中。這樣,該session以后再執(zhí)行相同的SQL語句時(shí),會(huì)直接從PGA里取出執(zhí)行計(jì)劃,跳過硬解析的所有步驟,這是最高效的解析方式,但是會(huì)消耗很大的內(nèi)存。俗稱為軟軟解析 。
Reload —
關(guān)于子游標(biāo)新建和reload 的區(qū)別,如果各版本的子游標(biāo)都不能被共享,那么會(huì)創(chuàng)建一個(gè)新的子游標(biāo) (new create) ,這種情況指的就是 environment 或綁定變量長度不一樣等 導(dǎo)致的情況。 而reload 指的是父游標(biāo)可以共享,同樣的子游標(biāo) (執(zhí)行計(jì)劃,environment 或bind var 等都一樣)原來已經(jīng)存在于library cache, 因?yàn)槟撤N原因被aged out出去,而現(xiàn)在需要它了,要重新裝載回來 (Oracle數(shù)據(jù)庫可能在某個(gè)地方保存了原來相同的子游標(biāo)信息)。
在Hash bucket中查找SQL,如果有的話就算作是一次get,并查找這個(gè)SQL語句的執(zhí)行計(jì)劃,如果執(zhí)行計(jì)劃已經(jīng)不存在了(age out)或者是存在但不可用(Invalidation),那么就必須對(duì)這條sql語句重新裝載,這就叫reload,如果執(zhí)行計(jì)劃存在并且可用的話,oracle就執(zhí)行這句話,這就叫做execution
五, 綁定變量(Bind Variables)
優(yōu)點(diǎn): 共享游標(biāo),減少硬解析
綁定變量分級(jí) —
前面說到執(zhí)行環(huán)境的變化比如綁定變量定義的類型大小不同會(huì)導(dǎo)致生成不同的游標(biāo),為了使游標(biāo)的數(shù)量不至于太多,產(chǎn)生了這個(gè)功能。此功能將變量的長度分為4個(gè)級(jí)別,0-32字節(jié),33-128字節(jié),129-2000字節(jié),>2000字節(jié) 這四個(gè)等級(jí)。不用說,同一個(gè)綁定變量(長度)的變化,最多能生成4個(gè)游標(biāo)。
缺點(diǎn): 綁定變量也有缺點(diǎn)。缺點(diǎn)就是,相對(duì)于字面量而言,會(huì)減弱查詢優(yōu)化器的功能。
比如:
select count(*) from t where id > 10;
select count(*) from t where id > 99999;
根據(jù)id值10,99999和表的統(tǒng)計(jì)信息,查詢優(yōu)化器可能會(huì)選擇全表掃描或者索引掃描,是合理的。
使用了綁定變量,優(yōu)化器會(huì)忽略他們的具體值,從而生成相同的執(zhí)行計(jì)劃。為了解決這個(gè)問題,
oracle9i引入了綁定變量窺測(cè)(bind variable peeking)的功能。
綁定變量窺測(cè)的優(yōu)點(diǎn),就是窺測(cè)綁定變量的值,把它們當(dāng)做字面量來使用。這樣的好處,就是能獲得最優(yōu)查詢路徑,比如是選擇全表掃描還是索引掃描。
綁定變量窺測(cè)也有缺點(diǎn),即生成的執(zhí)行計(jì)劃依賴第一次生成執(zhí)行計(jì)劃時(shí)所提供的值。舉例來說,就是如果第一次是全掃描,以后永遠(yuǎn)都是全表掃描了。這個(gè)方法對(duì)于非OLTP系統(tǒng)的缺點(diǎn)非常明顯,因?yàn)橐粋€(gè)綁定變量集可能返回的結(jié)果集只包含幾百行的數(shù)據(jù),而另一套綁定變量可能返回幾百萬行數(shù)據(jù),因此,Oracle建議保留CURSOR_SHARING作為該初始化參數(shù)的默認(rèn)值,以強(qiáng)制產(chǎn)生一個(gè)新的更有效的執(zhí)行計(jì)劃 (cursor_sharing的詳細(xì)解釋見后面)。
那么如何避免這個(gè)缺點(diǎn)呢?只有升級(jí)到oracle11g了。
oracle11g引用一個(gè)新功能,自適應(yīng)游標(biāo)共享(ACS)。這個(gè)功能就是根據(jù)綁定變量的值,可以為相同的sql語句,生成不同子游標(biāo),及不同的執(zhí)行計(jì)劃。ACS使用了兩個(gè)新的度量機(jī)制:綁定敏感度和綁定感知。具體可以參考Oracle11g文檔。
什么時(shí)候不使用綁定變量?
批量任務(wù)處理,報(bào)表生成,運(yùn)用OLAP的數(shù)據(jù)倉庫,因?yàn)檫@種大型的查詢時(shí)間較長,一次新的硬解析相對(duì)于這個(gè)查詢時(shí)間不算什么, 所以不用綁定變量沒有什么影響 。如果使用綁定變量,10g或以前的版本,一旦第一次執(zhí)行時(shí)綁定變量第一次提供的值如果是小范圍的,那么可能是索引掃描,但是第二次可能是數(shù)據(jù)倉庫典型的大時(shí)間范圍的查詢,需要全表掃描,但是還是沿用了前面的索引掃描,這樣導(dǎo)致性能下降。OLTP類型大多數(shù)是小量密集的操作,所以使用綁定變量時(shí)相對(duì)最優(yōu)的執(zhí)行計(jì)劃比較穩(wěn)定 。
在我們不使用where等條件判斷時(shí)我們就要盡量使用綁定變量(比如普通insert操作),沒理由不使用綁定變量; 而涉及到基數(shù)選擇性判斷時(shí)我們應(yīng)該盡量避免使用綁定變量,因?yàn)樵谖锢韮?yōu)化階段的綁定變量窺測(cè)遇到較大負(fù)面風(fēng)險(xiǎn)。
也可以參考下面的兩種建議:
如果sql處理的數(shù)據(jù)較少, 解析時(shí)間顯然比執(zhí)行時(shí)間多很多了,那么我們應(yīng)該盡量使用綁定變量,這種適用于 OLTP(聯(lián)機(jī)事務(wù)處理系統(tǒng));
而如果是數(shù)據(jù)倉庫類型的數(shù)據(jù)庫,我們對(duì)綁定變量的使用就應(yīng)該慎重了,因?yàn)檫@時(shí)的執(zhí)行時(shí)間有可能遠(yuǎn)遠(yuǎn)大于解析時(shí)間,解析時(shí)間相對(duì)于執(zhí)行時(shí)間近乎可以忽略,所以這時(shí)應(yīng)該盡量不使用綁定變量。
參數(shù)CURSOR_SHARING —
oracle是為了滿足一些以前開發(fā)的程序,里面有大量的相似的statement,沒有很好的使用綁定變量,但是重寫有不現(xiàn)實(shí)的情況下使用的一個(gè)參數(shù)。并且oracle也不建議修改這個(gè)參數(shù)。保持默認(rèn)即可。
語法 CURSOR_SHARING = { SIMILAR | EXACT | FORCE } ,默認(rèn)值為 EXACT
EXACT —
僅僅允許絕對(duì)一樣的SQL語句共享同樣的游標(biāo)。當(dāng)一個(gè)SQL語句解析的時(shí)候,首先到shared pool區(qū)查看是否有完全一樣的語句存在,如果不存在(其實(shí)此時(shí)是找不到共享的父游標(biāo)),就執(zhí)行hard parse .
SIMILAR —
如果在shared pool中無法找到完全一樣的語句的時(shí)候,就會(huì)在shared pool進(jìn)行一次新的查找,就是查找和當(dāng)前要解析的語句相似的SQL語句。 similar語句就是除了value of some literal不同,別的地方都相同的語句。比如下面:
select * from a where age=2;
select * from a where age=5;
如果在shared pool中查找到這樣的語句,就會(huì)做下一步的檢查,看shared pool中緩存的這個(gè)語句的execution plan是否適合當(dāng)前解析的語句,如果適合,就會(huì)使用shared pool的語句,而不去做hard parse。
FORCE —
強(qiáng)制將字面值不一樣的但是其他方面是一樣的SQL語句共享游標(biāo)。如果cursor_sharing設(shè)置為force的時(shí)候,當(dāng)在shared pool中發(fā)現(xiàn)了similar statement之后,就不會(huì)再去檢查執(zhí)行計(jì)劃了,而直接使用在shared pool中的這個(gè)語句了。
將cursor_sharing設(shè)置為force實(shí)際上是危險(xiǎn)的。這會(huì)可能形成suboptimal的執(zhí)行計(jì)劃。比如對(duì)于一個(gè)范圍查找的語句,比如select * from a where a>10 and a<20這樣類型的語句,緩存中的語句的執(zhí)行計(jì)劃可能對(duì)于正在解析的語句就是不適合的,不是最優(yōu)的執(zhí)行計(jì)劃。這樣看起來是減少了解析的時(shí)間,但是大大增大了execution的時(shí)間。
什么時(shí)候需要修改這個(gè)參數(shù)呢?需要滿足以下的條件:
一個(gè)是由于大量的shared pool hitmis影響了用戶的響應(yīng)時(shí)間(就是當(dāng)前的shared pool無法滿足共享sql語句存儲(chǔ)的需要),如果沒有這個(gè)問題,那么設(shè)置這個(gè)參數(shù),可能會(huì)造成更糟糕的性能。這個(gè)參數(shù)僅僅只是減少parse的時(shí)間。另外一個(gè)就是在現(xiàn)有程序中有大量的similar statement,可通過設(shè)置這個(gè)參數(shù)來獲得相對(duì)比較好的性能。
—————————————————————
關(guān)于cursor_sharing = similar的測(cè)試 :
http://www.wangchao.net.cn/bbsdetail_60551.html
若存在object_id的 histograms ,則每次是不同的 值 的時(shí)候都產(chǎn)生硬解析 ,若不存在 histograms ,則不產(chǎn)生硬解析 。換句話說,當(dāng)表的字段被分析過存在histograms的時(shí)候,similar 的表現(xiàn)和exact一樣,當(dāng)表的字段沒被分析不存在histograms的時(shí)候,similar的表現(xiàn)和force一樣。這樣避免了一味地如force一樣轉(zhuǎn)換成變量形式,因?yàn)橛衕ostograms的情況下轉(zhuǎn)換成變量之后就容易產(chǎn)生錯(cuò)誤的執(zhí)行計(jì)劃,沒有利用上統(tǒng)計(jì)信息。而exact呢,在沒有histograms的情況下也要分別產(chǎn)生硬解析,這樣的話,由于執(zhí)行計(jì)劃不會(huì)受到數(shù)據(jù)分布的影響(因?yàn)闆]有統(tǒng)計(jì)信息)重新解析是沒有實(shí)質(zhì)意義的。而similar則綜合了兩者的優(yōu)點(diǎn)。
備注: cursor_sharing=force or similar時(shí),在9205以下的版本BUG不少 。
—————————————————————
Library cache內(nèi)部機(jī)制詳解 參考:
http://www.hellodba.net/2010/07/oracle-library-cache.html
更多關(guān)于云服務(wù)器,域名注冊(cè),虛擬主機(jī)的問題,請(qǐng)?jiān)L問三五互聯(lián)官網(wǎng):m.shinetop.cn