為了將使用者寫的SQL文本轉化為Oracle認識的且可執行檔語句,這個過程就叫做解析過程。解析分為硬解析和軟解析。一條SQL語句在第一次被執行時必須進行硬解析。
當用戶端發出一條SQL語句(也可以是一個預存程序或者一個匿名PL/SQL塊)進入shared pool時(注意,我們從前面已經知道,Oracle對這些SQL不叫做SQL語句,而是稱為遊標。因為Oracle在處理SQL時,需要很多相關的輔助資訊,這些輔助資訊與SQL語句一起組成了遊標), Oracle首先將SQL文本轉化為ASCII值,然後根據hashFunction Compute其對應的hash值(hash_value)。根據計算出的hash值到library cache中找到對應的bucket,然後比較bucket裡是否存在該SQL語句。如果不存在,則需要按照我們前面所描述的,獲得shared pool latch,然後在shared pool中的可用chunk鏈表(也就是bucket)上找到一個可用的chunk,之後釋放shared pool latch。在獲得了chunk以後,這塊chunk就可以認為是進入了library cache。接下來,進行硬解析過程。硬解析包括以下幾個步驟。
對SQL語句進行文法檢查,看是否有文法錯誤。比如沒有寫from、select拼字錯誤等。如果存在文法錯誤,則退出解析過程。到資料字典裡校正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
查看本欄目更多精彩內容:http://www.bianceng.cnhttp://www.bianceng.cn/database/Oracle/