Oracle綁定變量類型和長度引起的執行計划變化


請關注個人小站:http://sqlhis.com/

在Oracle數據庫中執行SQL語句,當客戶端發出一條語句交付到ORACLE,會進行以下幾個步驟:

1、語法檢查(syntax check)
檢查此sql的拼寫是否語法。
2、語義檢查(semantic check)
諸如檢查sql語句中的訪問對象是否存在及該用戶是否具備相應的權限。
3、對sql語句進行解析(prase)
利用內部算法對sql進行解析,生成解析樹(parse tree)及執行計划(execution plan)。
4、執行sql,返回結果(execute and return)

其中,軟、硬解析就發生在第三個過程里。

如果使用了綁定變量,且綁定變量類型一直未變話,則通常只在第一次執行的時候進行一次硬解析(優化器創建解析樹、生成執行計划),后續都是軟解析(將此SQL和cache中的進行比較,如果相同,取已生成的執行計划),創建解析樹、生成執行計划對於sql的執行來說是開銷昂貴的動作,所以,應當極力避免硬解析,盡量使用軟解析。

本范例想說明:即便SQL語句完全相同,但是如果綁定變量的類型或者長度發生了變化的話,也會發生硬解析.

 建立測試表並清理緩存

--建立測試表
create table TESTBIND
(
  aaa CHAR(10),
  bbb CHAR(100),
  ccc CHAR(2000)
)


--清理緩存
ALTER SYSTEM FLUSH SHARED_POOL;
alter system flush BUFFER_CACHE;

 

執行一次插入操作

--第一次綁定
DECLARE v_AAA CHAR(10):='A';
        v_BBB CHAR(10):='B';
        v_CCC CHAR(10):='C';
BEGIN    
    INSERT INTO TESTBIND VALUES(v_AAA,v_BBB,v_CCC);
    COMMIT;
END;

 

查找被緩存的執行計划

SELECT SQL_TEXT,SQL_ID,LOADED_VERSIONS,OPEN_VERSIONS,EXECUTIONS, CHILD_NUMBER
FROM V$sql
WHERE sql_text LIKE  '%INSERT INTO TESTBIND%'
AND sql_TEXT NOT LIKE '%DECLARE%'

 返回結果如圖:

SQL_ID:這段SQL的唯一ID

EXECUTIONS:這段語句執行次數

 多次執行上面的插入語句並檢查V$SQL緩存,發現EXECUTIONS不斷增加,這說明后續都是執行的軟解析,即復用了第一次生成的執行計划

 

 再次執行插入操作,這次改變綁定變量的類型,v_AAA的類型從CHAR(10) 改為了VARCHAR(10)

--第二次綁定
DECLARE v_AAA VARCHAR(10):='A';
        v_BBB CHAR(10):='B';
        v_CCC CHAR(10):='C';
BEGIN    
    INSERT INTO TESTBIND VALUES(v_AAA,v_BBB,v_CCC);
    COMMIT;
END;

然后再次查看V$SQL,發現現在有兩條記錄,也就是說,同一個語句,由於傳入的綁定變量類型不同,在數據庫中有兩個執行計划,執行計划可以通過CHILD_NUMBER來區分

SELECT SQL_TEXT,SQL_ID,LOADED_VERSIONS,OPEN_VERSIONS,EXECUTIONS, CHILD_NUMBER
FROM V$sql
WHERE sql_text LIKE  '%INSERT INTO TESTBIND%'
AND sql_TEXT NOT LIKE '%DECLARE%'

 

為了看的更清楚一點,我們觀察v$sql_shared_cursor,這個視圖會說明兩個執行計划不一致的原因,v$sql_shared_cursor有很多不同的列,標識了各種執行計划不能復用的原因,由於我們今天只測試改變綁定變量類型和長度,所以只需要關注:

BIND_MISMATCH:當為Y的時候表示綁定變量類型不一致

BIND_LENGTH_UPGRADEABLE:當為Y的時候表示綁定變量類型的長度發生了變化

REASON:一個XML輸出,網上搜索了一遍,也沒看到這個XML的解釋,總之比較奇怪,不太看的明白

SELECT SQL_ID,CHILD_NUMBER,BIND_MISMATCH,BIND_LENGTH_UPGRADEABLE,REASON FROM v$sql_shared_cursor
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

查詢結果如下:BIND_MISMATCH=Y,表示綁定變量的類型發生了變化

REASON字段的XML,除了標紅的Bind mismatch(8)可以理解為綁定變量不一致外,其他都不太理解,且找了一輪沒有注釋

<ChildNode>
<ChildNumber>0</ChildNumber>
<ID>40</ID>
<reason>Bind mismatch(8)</reason>
<size>4x4</size>
<bind_position>0</bind_position>
<original_oacflg>19</original_oacflg>
<original_oacdty>96</original_oacdty>
<new_oacdty>1</new_oacdty>
</ChildNode>

 

從視圖可以查看每個查詢計划綁定變量的類型

SELECT * FROM v$sql_bind_capture
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

根據CHILD_NUMBER區分,可以看出參數B3 的類型發生了變化,注意這里類型的長度都是32,后面會說到原因

查詢具體的執行計划可以通過以下語句,第一個參數是SQL_ID,第二個參數是CHILD_NUMBER

select  * from table(dbms_xplan.display_cursor('4yddzp87tmzza',0)); 
select  * from table(dbms_xplan.display_cursor('4yddzp87tmzza',1)); 

 

再次執行插入語句,這次將V_AAA類型的長度從10改為33

--第三次綁定
DECLARE v_AAA VARCHAR(33):='A';
        v_BBB CHAR(10):='B';
        v_CCC CHAR(10):='C';
BEGIN    
    INSERT INTO TESTBIND VALUES(v_AAA,v_BBB,v_CCC);
    COMMIT;
END;

 

 再次通過語句檢查執行計划和綁定變量類型

SELECT SQL_TEXT,SQL_ID,LOADED_VERSIONS,OPEN_VERSIONS,EXECUTIONS, CHILD_NUMBER
FROM V$sql
WHERE sql_text LIKE  '%INSERT INTO TESTBIND%'
AND sql_TEXT NOT LIKE '%DECLARE%'

SELECT SQL_ID,CHILD_NUMBER,BIND_MISMATCH,BIND_LENGTH_UPGRADEABLE,REASON FROM v$sql_shared_cursor
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

SELECT * FROM v$sql_bind_capture
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

V$SQL視圖,可見又多了一個執行計划

v$sql_shared_cursor視圖,可見BIND_LENGTH_UPGRADEABLE=Y,即表示重新編譯原因是由於綁定變量的長度發生變化引起

v$sql_bind_capture視圖,可見數據長度從32變為128

 

 再次執行一個插入語句

--第四次綁定
DECLARE v_AAA VARCHAR(129):='A';
        v_BBB CHAR(10):='B';
        v_CCC CHAR(10):='C';
BEGIN    
    INSERT INTO TESTBIND VALUES(v_AAA,v_BBB,v_CCC);
    COMMIT;
END;

可見一一共4個執行計划,每個插入都是不同的執行計划

解釋了每次重新編譯的原因

每次綁定變量的差異,其中關注一下類型的長度分別是32,128,2000,也就是根據傳入類型的長度進行了區間划分

1-32分配到32

33到128分配到128

129到2000分配到2000,

這樣的話,不會每改變一次傳入變量長度執行計划就編譯一次

 

 可以試下將綁定變量V_AAA長度設置為1000

--第五次綁定
DECLARE v_AAA VARCHAR(1000):='A';
        v_BBB CHAR(10):='B';
        v_CCC CHAR(10):='C';
BEGIN    
    INSERT INTO TESTBIND VALUES(v_AAA,v_BBB,v_CCC);
    COMMIT;
END;

無論執行多少次,都不會有新的執行計划產生,實際上使用了VARCHAR2(2000)這個參數相關的執行計划

 

 

最后匯總以下查詢語句:

--檢查緩存
SELECT SQL_TEXT,SQL_ID,LOADED_VERSIONS,OPEN_VERSIONS,EXECUTIONS, CHILD_NUMBER
FROM V$sql
WHERE sql_text LIKE  '%INSERT INTO TESTBIND%'
AND sql_TEXT NOT LIKE '%DECLARE%'

--v$SQL的按SQL_ID的匯總表
SELECT * FROM v$sqlarea
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

--相同語句使用不同執行計划的具體原因
SELECT SQL_ID,CHILD_NUMBER,BIND_MISMATCH,BIND_LENGTH_UPGRADEABLE,REASON FROM v$sql_shared_cursor
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

--不同執行計划對應的具體綁定變量
SELECT * FROM v$sql_bind_capture
WHERE sql_id IN (SELECT sql_ID FROM V$sql WHERE sql_text LIKE  '%INSERT INTO TESTBIND%' AND sql_TEXT NOT LIKE '%DECLARE%')

--查詢具體的執行計划,第一個參數是SQL_ID,第二個參數和是CHILD_NUMBER
select  * from table(dbms_xplan.display_cursor('4yddzp87tmzza',0)); 
select  * from table(dbms_xplan.display_cursor('4yddzp87tmzza'

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM