Oracle中SQL語句解析的步驟(轉)

achilly發表於2009-11-24
我們都知道在Oracle中每條語句在執行之前都需要經過解析,這裡面又分為軟解析和硬解析。那麼這兩種解析有何不同之處呢?它們又分別是如何進行解析呢?Oracle內部解析的步驟又是如何進行的呢?下面我們就這些話題進行共同探討。 在Oracle中存在兩種型別的SQL語句,一類為DDL語句,他們是從來不會共享使用的,也就是每次執行都需要進行硬解析。還有一類就是DML語句,他們會根據情況選擇要麼進行硬解析,要麼進行軟解析。在Oracle 8i OCP教材的023中1-12有說明SQL語句的解析步驟,當一條SQL語句從客戶端程式傳遞到伺服器端程式後,需要執行如下步驟:
• 在共享池中搜尋 SQL 語句的現有副本
• 驗證 SQL 語句的語法是否準確
• 執行資料字典查詢來驗證表和列的定義
• 獲取物件的分析鎖以便在語句的分析過程中物件的定義不會改變
• 檢查使用者訪問引用方案物件的許可權
• 確定語句的最佳執行計劃
• 將語句和執行計劃載入共享的 SQL 區
這個先入為主的概念一直佔據著我的腦海,我認為硬解析就是上面幾個步驟。相對於硬解析,軟解析的步驟就是上面第一步找到現有SQL語句的副本後,只需要驗證使用者是否有許可權執行就是了,這樣省略上面好幾個步驟,相對硬解析來說效能開銷就非常小了。即使是在論壇上和大家討論時,我也一直堅持這個看法。直到前一天看了Tom的《Effective Oracle By Design》中關於語句處理的章節後,我才知道這個自己一直堅持的觀點事實上是錯誤的。
事實上,在Oracle中SQL語句的解析步驟如下:
1、 語法檢測。判斷一條SQL語句的語法是否符合SQL的規範,比如執行:SQL> selet * from emp;我們就可以看出由於Select關鍵字少了一個“c”,這條語句就無法透過語法檢驗的步驟了。
2、 語義檢查。語法正確的SQL語句在解析的第二個步驟就是判斷該SQL語句所訪問的表及列是否準確?使用者是否有許可權訪問或更改相應的表或列?比如如下語句:
SQL> select * from emp;
select * from emp
*
ERROR at line 1:
ORA-00942: table or view does not exist

由於查詢使用者沒有可供訪問的emp物件,因此該SQL語句無法透過語義檢查。
3、 檢查共享池中是否有相同的語句存在。假如執行的SQL語句已經在共享池中存在同樣的副本,那麼該SQL語句將會被軟解析,也就是可以重用已解析過的語句的執行計劃和最佳化方案,可以忽略語句解析過程中最耗費資源的步驟,這也是我們為什麼一直強調避免硬解析的原因。這個步驟又可以分為兩個步驟:
(1)驗證SQL語句是否完全一致。在這個步驟中,Oracle將會對傳遞進來的SQL語句使用HASH函式運算得出HASH值,再與共享池中現有語句的HASH值進行比較看是否一一對應。現有中SQL語句的HASH值我們可以透過訪問v$sql、v$sqlarea、v$sqltext等資料字典中的HASH_VALUE列查詢得出。如果SQL語句的HASH值一致,那麼ORACLE事實上還需要對SQL語句的語義進行再次檢測,以決定是否一致。那麼為什麼Oracle需要再次對語句文字進行檢測呢?不是SQL語句的HASH值已經對應上了?事實上就算是SQL語句的HASH值已經對應上了,並不能說明這兩條SQL語句就已經可以共享了。我們首先參考如下一個例子:假如使用者A有自己的一張表EMP,他要執行查詢語句:select * from emp;使用者B也有一張EMP表,同樣要查詢select * from emp;這樣他們兩條語句在文字上是一模一樣的,他們的HASH值也會一樣,但是由於涉及到查詢的相關表不一樣,他們事實上是無法共享的。假如這時候使用者C又要查詢同樣一條語句,他查詢的表為scott下的公有同義詞,還有就是SCOTT也查詢同樣一張自己的表emp,情況會是如何呢?
SQL> connect a/a
Connected.
SQL> create table emp ( x int );

Table created.

SQL> select * from emp;

no rows selected
SQL> connect b/b
Connected.
SQL> create table emp ( x int );

Table created.

SQL> select * from emp;

no rows selected

SQL> conn scott/tiger
Connected.
SQL> select * from emp;
SQL> conn c/c
Connected.
SQL> select * from emp;
SQL> conn/as sysdba
Connected.
SQL> select address,hash_value, executions, sql_text
2 from v$sql
3 where upper(sql_text) like 'SELECT * FROM EMP%'
4 /

ADDRESS HASH_VALUE EXECUTIONS SQL_TEXT
-------- ---------- ---------- ------------------------
78B89E9C 3011704998 1 select * from emp
78B89E9C 3011704998 1 select * from emp
78B89E9C 3011704998 2 select * from emp

我們可以看到這四個查詢的語句文字和HASH值都是一樣的,但是由於查詢的物件不同,只有後面兩個語句是可以共享的,不同情況的語句還是需要硬解析的。因此在檢查共享池共同SQL語句的時候,是需要根據具體情況而定的。
我們可以進一步查詢v$sql_shared_cursor以得知SQL為何不能共享的原因:
SQL> select kglhdpar, address,
2 auth_check_mismatch, translation_mismatch
3 from v$sql_shared_cursor
4 where kglhdpar in
5 ( select address
6 from v$sql
7 where upper(sql_text) like 'SELECT * FROM EMP%' )
8 /

KGLHDPAR ADDRESS A T
-------- -------- - -
78B89E9C 786C9D78 N N
78B89E9C 786AC810 Y Y
78B89E9C 786A11A4 Y Y

TRANSLATION_MISMATCH表示SQL遊標涉及到的資料物件是不同的;AUTH_CHECK_MISMATCH表示對同樣一條SQL語句轉換是不匹配的。
(2、)驗證SQL語句執行環境是否相同。比如同樣一條SQL語句,一個查詢會話加了/*+ first_rows */的HINT,另外一個使用者加/*+ all_rows */的HINT,他們就會產生不同的執行計劃,儘管他們是查詢同樣的資料。我們下面就一個例項來說明SQL執行環境對解析的影響,我們透過將會話的workarea_size_policy變更來檢視對同樣一條SQL語句執行的影響:
SQL> alter system flush shared_pool;

System altered.

SQL> show parameter workarea_size_policy

NAME TYPE VALUE
------------------------------------ ----------- --------------
workarea_size_policy string AUTO

SQL> select count(*) from t;

COUNT(*)
----------
5736

SQL> alter session set workarea_size_policy=manual;

Session altered.

SQL> select count(*) from t;

COUNT(*)
----------
5736

SQL> select sql_text, child_number, hash_value, address
2 from v$sql
3 where upper(sql_text) = 'SELECT COUNT(*) FROM T'
4 /

SQL_TEXT CHILD_NUMBER HASH_VALUE ADDRESS
------------------------------ ------------ ---------- --------
select count(*) from t 0 2199322426 78717328
select count(*) from t 1 2199322426 78717328

可以看到由於不同會話workarea_size_policy設定得不同,即使是同樣一條SQL語句還是無法共享的。透過進一步查詢v$sql_shared_cursor我們可以發現兩個會話的最佳化器環境是不同的:
SQL> select optimizer_mismatch
2 from v$sql_shared_cursor
3 where kglhdpar in
4 ( select address
5 from v$sql
6 where upper(sql_text) = 'SELECT COUNT(*) FROM T' );

O
-
N
Y

透過如上三個步驟檢查以後,如果SQL語句是一致的,那麼就會重用原有SQL語句的執行計劃和最佳化方案,也就是我們通常所說的軟解析。如果SQL語句沒有找到同樣的副本,那麼就需要進行硬解析了。
4、 Oracle根據提交的SQL語句再查詢相應的資料物件是否有統計資訊。如果有統計資訊的話,那麼CBO將會使用這些統計資訊產生所有可能的執行計劃(可能多達成千上萬個)和相應的Cost,最終選擇Cost最低的那個執行計劃。如果查詢的資料物件無統計資訊,則按RBO的預設規則選擇相應的執行計劃。這個步驟也是解析中最耗費資源的,因此我們應該極力避免硬解析的產生。至此,解析的步驟已經全部完成,Oracle將會根據解析產生的執行計劃執行SQL語句和提取相應的資料。

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

相關文章