解析SQL語句的過程

爲了將用戶寫的SQL文本轉化爲Oracle認識的且可執行的語句,這個過程就叫作解析過程。解析分爲硬解析和軟解析。一條SQL語句在第一次被執行時必須進行硬解析。sql

  當客戶端發出一條SQL語句(也能夠是一個存儲過程或者一個匿名PL/SQL塊)進入shared pool時(注意,咱們從前面已經知道,Oracle對這些SQL不叫作SQL語句,而是稱爲遊標。由於Oracle在處理SQL時,須要不少相關的輔助信息,這些輔助信息與SQL語句一塊兒組成了遊標),Oracle首先將SQL文本轉化爲ASCII值,而後根據hash函數計算其對應的hash值(hash_value)。根據計算出的hash值到library cache中找到對應的bucket,而後比較bucket裏是否存在該SQL語句。session

  若是不存在,則須要按照咱們前面所描述的,得到shared pool latch,而後在shared pool中的可用chunk鏈表(也就是bucket)上找到一個可用的chunk,以後釋放shared pool latch。在得到了chunk之後,這塊chunk就能夠認爲是進入了library cache。接下來,進行硬解析過程。硬解析包括如下幾個步驟。oracle

  對SQL語句進行文法檢查,看是否有文法錯誤。好比沒有寫from、select拼寫錯誤等。若是存在文法錯誤,則退出解析過程。ide

到數據字典裏校驗SQL語句涉及的對象和列是否都存在。若是不存在,則退出解析過程。這個過程會加載dictionary cache。函數

  將對象進行名稱轉換。好比將同名詞翻譯成實際的對象等。好比select * from t中,t是一個同名詞,指向hr.t1,因而Oracle將t轉換爲hr.t1。若是轉換失敗,則退出解析過程。測試

  檢查發出SQL語句的用戶是否具備訪問SQL語句裏所引用的對象的權限。若是沒有權限,則退出解析過程。優化

  經過優化器建立一個最優的執行計劃。這個過程會根據數據字典裏記錄的對象的統計信息,來計算最優的執行計劃。這一步牽涉大量數學運算,是最消耗CPU資源的。翻譯

 將該遊標所產生的執行計劃、SQL文本等裝載進library cache的heap中。視頻

  在硬解析的過程當中,進程會一直持有library cache latch,直到硬解析結束爲止。硬解析結束之後,會爲SQL語句產生兩個遊標,一個是父遊標,另外一個是子游標。父遊標裏主要包含兩種信息:SQL文本以及優化目標(optimizer goal)。父遊標在第一次打開時被鎖定,直到其餘全部的session都關閉該遊標後才被解鎖。當父遊標被鎖定的時候是不能被交換出library cache的,只有在解鎖之後才能被交換出library cache。父遊標被交換出內存時,父遊標對應的全部子游標也被交換出library cache。子游標包括遊標全部的信息,好比具體的執行計劃、綁定變量等。子游標隨時能夠被交換出library cache,當子游標被交換出library cache時,Oracle能夠利用父遊標的信息從新構建出一個子遊標來,這個過程叫reload。可使用下面的方式來肯定reload的比率:對象

select 100*sum(reloads)/sum(pins) Reload_Ratio from v$librarycache;

  一個父遊標能夠對應多個子遊標。子游標具體的個數能夠從視圖v$sqlarea的version_count字段體現出來。而每一個具體的子游標則全都在視圖v$sql裏體現。當具體綁定變量的值與上次綁定變量的值有較大差別(好比上次執行的綁定變量值的長度是6位,而此次執行綁定變量的值的長度是200位)時或者當SQL語句徹底相同,可是所引用的表屬於不一樣的用戶時,都會建立一個新的子游標。

  若是在bucket中找到了該SQL語句,則說明該SQL語句之前運行過,因而進行軟解析。軟解析是相對於硬解析而言的,若是解析過程當中,能夠從硬解析的步驟中去掉一個或多個的話,這樣的解析就是軟解析。軟解析分爲如下三種類型。

  第一種是某個session發出的SQL語句與library cache裏其餘session發出的SQL語句一致。這時,該解析過程當中能夠去掉硬解析中的 和 ,可是仍然要進行硬解析過程當中的 、 、,也就是表名和列名檢查、名稱轉換和權限檢查。

  第二種是某個session發出的SQL語句是該session以前發出的曾經執行過的SQL語句。這時,該解析過程當中能夠去掉硬解析中的、 、 和 這四步,可是仍然要進行權限檢查,由於可能經過grant改變了該session用戶的權限。

 第三種是當設置了初始化參數session_cached_cursors時,當某個session第三次執行相同的SQL時,則會把該SQL語句的遊標信息轉移到該session的PGA裏。這樣,該session之後再執行相同的SQL語句時,會直接從PGA裏取出執行計劃,從而跳過硬解析的全部步驟。這種狀況下,是最高效的解析方式,可是會消耗很大的內存。

 咱們舉一個例子來講明解析SQL語句的過程。在該測試中,綁定變量名稱相同,可是變量類型不一樣時,所出現的解析狀況。以下所示。

 首先,執行下面的命令,清空shared pool裏全部的SQL語句:

SQL> alter system flush shared_pool;

 而後,定義一個數值型綁定變量,併爲該綁定變數賦一個數值型的值之後,執行具體的查詢語句。

SQL> variable v_obj_id number;

SQL> exec :v_obj_id := 4474;

SQL> select object_id,object_name from sharedpool_test

where object_id=:v_obj_id;

OBJECT_ID     OBJECT_NAME

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

4474         AGGXMLIMP

接下來,定義一個字符型的綁定變量,變量名與前面相同,爲該綁定變數賦一個字符型的值之後,執行相同的查詢:

SQL> variable v_obj_id varchar2(10);

SQL> exec :v_obj_id := '4474';

SQL> select object_id,object_name from sharedpool_test

where object_id=:v_obj_id;

OBJECT_ID     OBJECT_NAME

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

4474         AGGXMLIMP

而後咱們到視圖v$sqlarea裏找到該SQL的父遊標的信息,併到視圖v$sql裏找該SQL的全部子游標的信息。


SQL> select sql_text,version_count from v$sqlarea where

sql_text like ‘%sharedpool_test%’;

SQL_TEXT                                                

      VERSION_COUNT          

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

select object_id,object_name from sharedpool_test where

object_id=:v_obj_id         2

SQL> select sql_text,child_address,address from v$sql

where sql_text like ‘%sharedpool_test%’;

SQL_TEXT                                                

CHILD_ADDRESS                                                                                                  ADDRESS

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

select object_id,object_name from sharedpool_test where

object_id=:v_obj_id                                                                                            6757F358 676B6D08

select object_id,object_name from sharedpool_test where

object_id=:v_obj_id                                                                                             674440FC 676B6D08

 從記錄父遊標的視圖v$sqlarea的version_count列能夠看到,該SQL語句有2個子遊標。而從記錄子游標的視圖v$sql裏能夠看到,該SQL文本確實有兩條記錄,並且它們的SQL文本所處的地址(ADDRESS列)也是同樣的,可是子地址(CHILD_ADDRESS)卻不同。這裏的子地址實際就是子游標所對應的heap 0的句柄。

 由此咱們也能夠看到,存在許多因素可能致使SQL語句不能共享。常見的因素包括SQL文本大小寫不一致、SQL語句的綁定變量的類型不一致、SQL語句涉及的對象名稱雖然一致可是位於不一樣的schema下、SQL的優化器模式不一致(好比添加提示、修改了optimizer_mode參數等)等


oracle視頻教程請關注:http://down.51cto.com/4202939/up

相關文章
相關標籤/搜索