繫結變數之繫結變數窺探(Bind Peeking)
繫結變數是Oracle解決硬解析的首要利器,能解決OLTP系統中共享池的過度耗用以提高系統效能。對於使用了繫結變數的SQL而言,具體有哪些因素決定其執行計劃?
1.繫結變數窺探
2.若未啟用繫結變數窺探,則對於哪些可選擇率會隨著具體輸入值的不同而不同的謂詞條件會使用預設的選擇率。
具體資料庫是否啟用繫結變數窺探(Bind Peeking)由隱含引數_OPTIM_PEEK_USER_BINDS決定,在9i版本之後該引數值預設為TRUE,代表繫結窺探已經啟用了。
啟用繫結變數窺探,Oracle會在第一次硬解析時根據WHERE條件計算出SELECTIVITY以及CARDINALITY,最大限度的得到SQL的準確執行計劃。但其最大問題在於,不管以後繫結變數傳入何值,它會一直沿用之前硬解析時產生的解析樹和執行計劃(針對於11g之前的版本,11g引入了ACS該問題得到了改善)。該問題可能會導致CBO在某些情況下所選擇的執行計劃並不是最優執行計劃,從而極大的影響了系統的效能。
操作環境:
點選(此處)摺疊或開啟
- SELECT * FROM v$version;
-
BANNER
-
------------------------------------------------------------------------------------------------------------------------------------------------------
-
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
-
PL/SQL Release 10.2.0.1.0 - Production
-
CORE 10.2.0.1.0 Production
-
TNS for Linux: Version 10.2.0.1.0 - Production
- NLSRTL Version 10.2.0.1.0 - Production
準備測試表:
點選(此處)摺疊或開啟
-
create table t as select * from dba_objects;
-
create index ind_obj_type on t(OBJECT_TYPE);
-
- update t set OBJECT_TYPE='VIEW' WHERE rownum<50598;
- select OBJECT_TYPE,count(*) FROM t group by OBJECT_TYPE;
-
OBJECT_TYPE COUNT(*)
-
--------------------------------------------------------- ----------
-
INDEX PARTITION 2
-
TABLE PARTITION 2
-
VIEW 50597
-
- exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'T',estimate_percent=>100,cascade=>TRUE,method_opt=>'FOR ALL COLUMNS');
可以看到在OBJECT_TYPE列存在著嚴重的傾斜,由於該列收集了直方圖資訊,因此在不使用繫結變數的情況下OBJECT_TYPE='INDEX PARTITION'和INDEX PARTITION='VIEW'可能會走不同的執行計劃。具體實驗如下:
點選(此處)摺疊或開啟
- select count(*) from t where object_type='INDEX PARTITION';
-
COUNT(*)
-
----------
- 2
-
- select count(*) from t where object_type='VIEW';
-
COUNT(*)
-
----------
-
50597
-
- select sql_text,sql_id,version_count from v$sqlarea where sql_text like 'select count(*) from t%';
-
SQL_TEXT SQL_ID VERSION_COUNT
-
------------------------------------------------------------ --------------------------------------- -------------
-
select count(*) from t where object_type='VIEW' 1615uttr9arqc 1
-
select count(*) from t where object_type='INDEX PARTITION' 7hyfwxx1r7pkw 1
-
-
- select * from table(dbms_xplan.display_cursor('7hyfwxx1r7pkw',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 7hyfwxx1r7pkw, child number 0
-
-------------------------------------
- select count(*) from t where object_type='INDEX PARTITION'
- Plan hash value: 3381337503
-
-----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
-
-----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | |1 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2 | INDEX RANGE SCAN | IND_OBJ_TYPE | 2 | 12 | 1 (0) | 00:00:01 |
-
-----------------------------------------------------------------------------------------------
-
- select * from table(dbms_xplan.display_cursor('1615uttr9arqc',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 1615uttr9arqc, child number 0
-
-------------------------------------
- select count(*) from t where object_type='VIEW'
- Plan hash value: 1419492071
-
-----------------------------------------------------------------------------------------------
-
| Id| Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | | 27 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2| INDEX FAST FULL SCAN | IND_OBJ_TYPE | 50597| 296K | 27 (4) | 00:00:01 |
- -----------------------------------------------------------------------------------------------
可以看到在不使用繫結變數的情況下,object_type='INDEX PARTITION'走的INDEX RANGE SCAN索引範圍掃描,object_type='VIEW'走的INDEX FAST FULL SCAN索引快速全掃描,執行計劃與實際資料的分佈情況相符。
現在我們將前面的兩條語句改造成使用繫結變數的等價形式。
點選(此處)摺疊或開啟
-
var v varchar2(30);
-
exec :v:='INDEX PARTITION';
-
select count(*) from t where object_type=:v;
-
COUNT(*)
-
----------
-
2
-
-
select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t%';
-
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-
------------------------------------------------------------ --------------------------------------- ------------- -----------
-
select count(*) from t where object_type=:v 2mbcpzxk94xyg 1 1
-
- select * from table(dbms_xplan.display_cursor('2mbcpzxk94xyg',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 2mbcpzxk94xyg, child number 0
-
-------------------------------------
- select count(*) from t where object_type=:v
- Plan hash value: 3381337503
-
-----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | | 1 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2 | INDEX RANGE SCAN | IND_OBJ_TYPE | 2 | 12 | 1 (0) | 00:00:01 |
-
-----------------------------------------------------------------------------------------------
-
Peeked Binds (identified by position):
- --------------------------------------
-
1 - :V (VARCHAR2(30), CSID=31): 'INDEX PARTITION'
-
-
exec :v :='VIEW';
-
select count(*) from t where object_type=:v;
-
COUNT(*)
-
----------
- 50597
-
- select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t%';
- SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
- ------------------------------------------------------------ --------------------------------------- ------------- -----------select count(*) from t where object_type=:v 2mbcpzxk94xyg 1 2
- select * from table(dbms_xplan.display_cursor('2mbcpzxk94xyg',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 2mbcpzxk94xyg, child number 0
-
-------------------------------------
- select count(*) from t where object_type=:v
- Plan hash value: 3381337503
-
----------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | | 1 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2 | INDEX RANGE SCAN | IND_OBJ_TYPE | 2 | 12 | 1 (0) | 00:00:01 |
-
----------------------------------------------------------------------------------------------
-
Peeked Binds (identified by position):
- --------------------------------------
- 1 - :V (VARCHAR2(30), CSID=31): 'INDEX PARTITION'
下面在做同樣的實驗,只不過把順序調整一下,先執行exec :v:='VIEW',然後再執行exec :v:='INDEX PARTITION', 看看具體效果。
為避免重用之前的解析樹以及執行計劃,首先重新整理一遍共享池(此步驟儘量不要在生產環境嘗試)
alter system flush shared_pool;
點選(此處)摺疊或開啟
-
var v varchar2(30);
-
exec :v:='VIEW';
-
select count(*) from t where object_type=:v;
-
COUNT(*)
-
----------
-
50597
-
- select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t%';
-
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-
------------------------------------------------------------ --------------------------------------- ------------- ----------
-
select count(*) from t where object_type=:v 2mbcpzxk94xyg 1 1
-
- select * from table(dbms_xplan.display_cursor('2mbcpzxk94xyg',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
---------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 2mbcpzxk94xyg, child number 0
-
-------------------------------------
- select count(*) from t where object_type=:v
- Plan hash value: 1419492071
-
----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | | 27 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2 | INDEX FAST FULL SCAN | IND_OBJ_TYPE | 50597 | 296K | 27 (4) | 00:00:01 |
-
----------------------------------------------------------------------------------------
-
Peeked Binds (identified by position):
- --------------------------------------
-
1 - :V (VARCHAR2(30), CSID=31): 'VIEW'
-
-
exec :v:='INDEX PARTITION';
-
select count(*) from t where object_type=:v;
- select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t%';
-
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-
------------------------------------------------------------ --------------------------------------- ------------- ----------
-
select count(*) from t where object_type=:v 2mbcpzxk94xyg 1 2
-
- select * from table(dbms_xplan.display_cursor('2mbcpzxk94xyg',0,'advanced'));
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 2mbcpzxk94xyg, child number 0
-
-------------------------------------
- select count(*) from t where object_type=:v
- Plan hash value: 1419492071
-
-----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | | 27 (100) | |
-
| 1 | SORT AGGREGATE | | 1 | 6 | | |
-
|* 2 | INDEX FAST FULL SCAN | IND_OBJ_TYPE | 16867 | 98K | 27 (4) | 00:00:01 |
- -------------------------------------------------------------------------------------------
透過以上的兩個實驗驗證了一個問題:當SQL語句中有繫結變數,不管以後繫結變數傳入何值,它會一直沿用之前硬解析時產生的解析樹和執行計劃.
有沒有辦法讓上述使用繫結變數改寫後的SQL語句再次執行時,選擇走索引快速全掃描呢?有,讓Oracle再次執行硬解析就可以,通常做法為對SQL涉及的表執行DDL操作,因為一旦某表執行了DDL操作,庫快取中所有SQL文字中包含了這個表的Cursor都會被Oracle標記為失效,Oracle再次執行與該表相關的SQL就會使用硬解析。最簡單的DDL操作就是新增註釋的COMMENT語句:
comment on table t is 'Bind Peeking Test';
再次執行
點選(此處)摺疊或開啟
- exec :v:='INDEX PARTITION';
-
select count(*) from t where object_type=:v;
-
select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t%';
-
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-
------------------------------------------------------------ --------------------------------------- ------------- ----------
- select count(*) from t where object_type=:v 2mbcpzxk94xyg 1 1
-
- select * from table(dbms_xplan.display_cursor('2mbcpzxk94xyg',0,'advanced'));
-
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 2mbcpzxk94xyg, child number 0
-------------------------------------
select count(*) from t where object_type=:v
Plan hash value: 3381337503
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | INDEX RANGE SCAN| IND_OBJ_TYPE | 2 | 12 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Peeked Binds (identified by position):
--------------------------------------
1 - :V (VARCHAR2(30), CSID=31): 'INDEX PARTITION'
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29827284/viewspace-2138319/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- oracle bind value peeking繫結變數窺視Oracle變數
- 繫結變數和BIND PEEKING變數
- Oracle 繫結變數窺探Oracle變數
- 10g繫結變數窺探變數
- 繫結變數窺測變數
- 繫結變數、BIND PEEKING、histogram(柱狀圖)的使用變數Histogram
- 繫結變數優缺點、使用、繫結變數窺探、 Oracle自適應共享遊標變數Oracle
- oracle 繫結變數(bind variable)Oracle變數
- 繫結變數窺測的演變變數
- 繫結變數、BIND PEEKING、histogram(柱狀圖)的使用(zt)變數Histogram
- Oracle 變數繫結與變數窺視合集Oracle變數
- oracle繫結變數窺視(zt)Oracle變數
- Event 10053 執行計劃 繫結變數 Bind peeking變數
- 繫結變數窺視測試案例變數
- 【SQL 調優】繫結變數窺測SQL變數
- Oracle之繫結變數Oracle變數
- 【原創】Oracle 變數繫結與變數窺視合集Oracle變數
- 繫結變數的正確使用,bind peeking,Adaptive Cursor Sharing變數APT
- 索引失效系列——繫結變數引起的peeking索引變數
- 繫結變數變數
- 如何解決執行計劃繫結變數bind peeking重新硬解析變數
- Oracle繫結變數分級(Bind Graduation)Oracle變數
- [SQL]oracle 繫結變數(bind variable)[轉載]SQLOracle變數
- Oracle 繫結變數Oracle變數
- v$sql_bind_capture 獲取繫結變數SQLAPT變數
- 繫結變數之基本概念變數
- 檢視繫結變數變數
- PLSQL使用繫結變數SQL變數
- 關於繫結變數變數
- 【優化】使用繫結變數 OR 不使用繫結變數,這不是問題!優化變數
- v$sql_bind_capture 獲取繫結變數資訊SQLAPT變數
- ORACLE 繫結變數用法總結Oracle變數
- 【最佳化】使用繫結變數 OR 不使用繫結變數,這不是問題!變數
- 繫結變數的測試變數
- 如何獲取繫結變數變數
- Oracle 繫結變數 詳解Oracle變數
- 使用繫結變數窺探後的cardinality和selectivity的計算方法變數
- 透過v$sql_bind_capture 檢視繫結變數。SQLAPT變數