繫結變數窺視測試案例
我們平時在sql中經常使用捆綁變數來使sql在shared pool中可以被重用,大大減少了sql的hard parse。oracle在hard parse一個sql的時候,如果收集了histogram的資訊,如果隱藏引數_optim_peek_user_binds被設定成true(default to true),
那麼cbo會去偷窺一下捆綁變數的值,來選擇一個更好的執行計劃。當然,捆綁變數的偷窺只發生在hard parse。
前期測試表格建立
CREATE TABLE TEST_20150518 AS SELECT * FROM DBA_OBJECTS; select count(*) from TEST_20150518; update TEST_20150518 set status ='VALID'; commit; select count(*) from TEST_20150518 where status='VALID'; update TEST_20150518 set status='INVALID' where rownum<=100; commit; update TEST_20150518 set STATUS='TEST' WHERE ROWNUM<=3; commit;
SQL> select count(*),status from TEST_20150518 group by status; COUNT(*) STATUS ---------- ------- 97 INVALID 3 TEST 50112 VALID create index idx_TEST_20150518 on TEST_20150518(status); 統計資訊包括柱狀圖收集 EXEC DBMS_STATS.GATHER_TABLE_STATS (ownname=>'sys',tabname=>'TEST_20150518',method_opt=>'FOR ALL INDEXED COLUMNS SIZE AUTO',estimate_percent=>100,cascade=>true); |
從以上可以看出TEST_20150518表中status='VALID'的有50112行,status='INVALID'的有97行,status='TEST'的有三行,如果select * from TEST_20150518
where status='VALID', 那麼必定是用全表掃描比較好,如果select * from TEST_20150518 where status='INVALID' 或者select * from TEST_20150518 where status='TEST'那麼必定是走索引比較好。
檢視柱狀圖的分佈情況 SQL> set line 150 SQL> col owner for a10 SQL> col table_name for a15 SQL> col column_name for a15 SQL> col ENDPOINT_ACTUAL_VALUE for a30 SQL> SELECT * FROM DBA_HISTOGRAMS WHERE table_name='TEST_20150518' and column_name='STATUS';
OWNER TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE ENDPOINT_ACTUAL_VALUE ---------- --------------- --------------- --------------- -------------- ------------------------------ SYS TEST_20150518 STATUS 97 3.8063E+35 SYS TEST_20150518 STATUS 100 4.3756E+35 SYS TEST_20150518 STATUS 50212 4.4786E+35 |
histogram 裡三個bucket明確表明了該列資料分佈情況.
繫結變數窺視測試
var test varchar2(10); exec :test:='VALID'; select object_id,status FROM TEST_20150518 where status=:test; SQL> select sql_id,CHILD_NUMBER from v$sql where sql_text like '%select object_id,status FROM TEST_20150518 where status%'; SQL_ID CHILD_NUMBER ------------- ------------ 82hsrmdvg3vna 0 select * from table(dbms_xplan.display_cursor('82hsrmdvg3vna',0,'ADVANCED')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------ SQL_ID 82hsrmdvg3vna, child number 0 ------------------------------------- select object_id,status FROM TEST_20150518 where status=:test
Plan hash value: 2123920744 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 156 (100)| | |* 1 | TABLE ACCESS FULL| TEST_20150518 | 50112 | 587K| 156 (2)| 00:00:02 | Peeked Binds (identified by position): -------------------------------------- PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------
1 - :TEST (VARCHAR2(30), CSID=852): 'VALID' |
可以看到,第一次硬解析,status=“valid” 時,走了正確的執行計劃
exec :test:='INVALID'; select object_id,status FROM TEST_20150518 where status=:test; SQL> select sql_id,CHILD_NUMBER from v$sql where sql_text like '%select object_id,status FROM TEST_20150518 where status%'; SQL_ID CHILD_NUMBER ------------- ------------ 82hsrmdvg3vna 0 select * from table(dbms_xplan.display_cursor('82hsrmdvg3vna',0,'ADVANCED')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------- SQL_ID 82hsrmdvg3vna, child number 0 ------------------------------------- select object_id,status FROM TEST_20150518 where status=:test
Plan hash value: 2123920744 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 156 (100)| | |* 1 | TABLE ACCESS FULL| TEST_20150518 | 50112 | 587K| 156 (2)| 00:00:02 | Peeked Binds (identified by position): -------------------------------------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------
1 - :TEST (VARCHAR2(30), CSID=852): 'VALID' |
可以看到,雖然繫結變數的值變了,但是oracle沒有硬解析,沒有重新偷窺繫結變數,還是走了錯誤的全表掃描。
重新整理共享池,使得語句重新硬解析
alter system flush shared_pool; 讓 sql 重新被硬解析 exec :test:='INVALID'; select object_id,status FROM TEST_20150518 where status=:test; select sql_id,CHILD_NUMBER,FIRST_LOAD_TIME,last_load_time,LAST_ACTIVE_TIME from v$sql where sql_text like '%select object_id,status FROM TEST_20150518 where status%'; SQL_ID CHILD_NUMBER FIRST_LOAD_TIME LAST_LOAD_TIME LAST_ACTI ------------- ------------ -------------------------------------- -------------------------------------- --------- d78upf6d4hpqw 0 2015-05-18/13:21:27 2015-05-18/13:21:27 18-MAY-15 82hsrmdvg3vna 0 2015-05-18/13:21:21 2015-05-18/13:21:21 18-MAY-15
select * from table(dbms_xplan.display_cursor('82hsrmdvg3vna',0,'ADVANCED')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------ SQL_ID 82hsrmdvg3vna, child number 0 ------------------------------------- select object_id,status FROM TEST_20150518 where status=:test;
Plan hash value: 498082260 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_20150518 | 97 | 1164 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- |* 2 | INDEX RANGE SCAN | IDX_TEST_20150518 | 97 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Peeked Binds (identified by position): --------------------------------------
1 - :TEST (VARCHAR2(30), CSID=852): 'INVALID' |
可以看到,sql被重新hard parse後,走了正確的索引路線
解決辦法
讓sql在shared pool裡失效,使用DBMS_SHARED_POOL.PURGE,或者alter system flush shared pool. 當然,後兩者不推薦,尤其是最後一個!!
不過,oracle 11g裡可以動態偷窺繫結變數,放多個執行計劃在shared pool, 對這個副作用做了很大改進。具體新特性可以去看11g文件。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23732248/viewspace-2777117/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 繫結變數窺測變數
- 繫結變數窺測的演變變數
- oracle繫結變數窺視(zt)Oracle變數
- Oracle 變數繫結與變數窺視合集Oracle變數
- 【SQL 調優】繫結變數窺測SQL變數
- 【原創】Oracle 變數繫結與變數窺視合集Oracle變數
- oracle bind value peeking繫結變數窺視Oracle變數
- Oracle 繫結變數窺探Oracle變數
- 繫結變數之繫結變數窺探(Bind Peeking)變數
- 繫結變數的測試變數
- oracle繫結變數的測試Oracle變數
- 10g繫結變數窺探變數
- SQL使用繫結變數,測試例項。SQL變數
- 檢視繫結變數變數
- oracle 11g 新特性之動態繫結變數窺視(二)Oracle變數
- oracle 11g 新特性之動態繫結變數窺視(一)Oracle變數
- 繫結變數優缺點、使用、繫結變數窺探、 Oracle自適應共享遊標變數Oracle
- PL/SQL中繫結變數使用的簡單測試SQL變數
- 檢視未繫結變數的sql變數SQL
- 繫結變數變數
- Oracle 繫結變數Oracle變數
- PLSQL使用繫結變數SQL變數
- Oracle之繫結變數Oracle變數
- 關於繫結變數變數
- 使用繫結變數窺探後的cardinality和selectivity的計算方法變數
- 【優化】使用繫結變數 OR 不使用繫結變數,這不是問題!優化變數
- ORACLE 繫結變數用法總結Oracle變數
- 【最佳化】使用繫結變數 OR 不使用繫結變數,這不是問題!變數
- oracle 繫結變數(bind variable)Oracle變數
- 如何獲取繫結變數變數
- Oracle 繫結變數 詳解Oracle變數
- 透過v$sql_bind_capture 檢視繫結變數。SQLAPT變數
- 通過v$sql_bind_capture 檢視繫結變數。SQLAPT變數
- 關於繫結變數的SQL繫結什麼值變數SQL
- Angular 測試小窺Angular
- MySQL高階特性——繫結變數MySql變數
- SQL Developer中使用繫結變數SQLDeveloper變數
- 繫結變數之基本概念變數