如今把隱含參數_optim_peek_user_binds設置爲false關閉綁定變量窺探:sql
SQL> alter session set "_optim_peek_user_binds" = false; 會話已更改。
而後保持x不變,y修改成1000,再次執行目標SQL:看到目標SQL對應列的version_count和executions值由以前的1變爲了2,說明Oracle在執行該SQL時用的仍是硬解析:緩存
SQL> exec :y := 1000;session
PL/SQL 過程已成功完成。ide
SQL> select count(*) from t1 where object_id between :x and :y;性能
COUNT(*)測試
----------orm
2blog
SQL> set linesi 200索引
SQL> select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t1%';字符串
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
----------------------------------------------------------------- --------------- ------------- ----------
select count(*) from t1 where object_id between 999 and 1000 5y9rdffcnchk6 1 1
select count(*) from t1 where object_id between 999 and 60000 b8xxw70vja3tn 1 1
select count(*) from t1 where object_id between :x and :y 9dhu3xk2zu531 2 2
這也就意味着SQL所在的parent cursor下掛了兩個child cursor:
SQL> select plan_hash_value,child_number from v$sql where sql_id='9dhu3xk2zu531'; PLAN_HASH_VALUE CHILD_NUMBER--------------- ------------ 1410530761 0 2351893609 1
而child_number爲1的child cursor對應執行計劃以下:
SQL> select * from table(dbms_xplan.display_cursor('9dhu3xk2zu531',1,
'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------
SQL_ID 9dhu3xk2zu531, child number 1
-------------------------------------
select count(*) from t1 where object_id between :x and :y
Plan hash value: 2351893609
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
|* 2 | FILTER | | | | | |
|* 3 | INDEX RANGE SCAN| IDX_T1 | 230 | 1150 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
DB_VERSION('12.1.0.2')
OPT_PARAM('_optim_peek_user_binds' 'false')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(:Y>=:X)
3 - access("OBJECT_ID">=:X AND "OBJECT_ID"<=:Y)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT(*)[22]
從上述顯示內容看出,以前走的是索引快速全掃描,而此時走的是索引範圍掃描,執行計劃中有 OPT_PARAM('_optim_peek_user_binds' 'false'),而並無出現以前啓用綁定邊路窺探的時候的「peeking binds」的內容,這說明已經禁用這個功能。
以前介紹使得Oracle執行目標SQL再一次使用硬解析是使用DDL操做,可是因爲其影響範圍大,可能形成必定的性能問題,如今再介紹一種方式僅限於目標SQL對應的shared cursor,也就是說能夠作到只讓Oracle在執行此目標SQL使用硬解析,執行其餘SQL都和原來同樣,保持不變。
這種方法使用DBMS_SHARED_POOL.PURGE。DBMS_SHARED_POOL.PURGE是從10.2.0.4引入的,用來刪除指定的緩存在庫緩存中的shared cursor,DBMS_SHARED_POOL.PURGE可讓Oracle在執行目標SQL使用硬解析,就是若是某個SQL對應shared cursor被刪除,那麼再次執行該SQL天然也就使用硬解析了。
以下查詢目標SQL對應address以及列hash_value:
SQL> select sql_text,sql_id,address,hash_value from v$sqlarea where sql_text like 'select count(*) from t1%';
SQL_TEXT SQL_ID ADDRESS HASH_VALUE
----------------------------------------------------------------- --------------- ---------------- ----------
select count(*) from t1 where object_id between 999 and 1000 5y9rdffcnchk6 00007FFD0FADBBB0 2571518534
select count(*) from t1 where object_id between 999 and 60000 b8xxw70vja3tn 00007FFCD84D4A60 924127028
select count(*) from t1 where object_id between :x and :y 9dhu3xk2zu531 00007FFD0F8F7170 2247955553
而後將其拼接成一個字符串輸入參數傳入DBMS_SHARED_POOL.PURGE並執行,第二個參數是常量c,表示要刪除的是shared cursor;
SQL> exec sys.dbms_shared_pool.purge('00007FFD0F8F7170,2247955553','c'); PL/SQL 過程已成功完成。
而後查看發現目標SQL對應的shared cursor確實被刪除:
SQL> select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t1%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
----------------------------------------------------------------- --------------- ------------- ----------
select count(*) from t1 where object_id between 999 and 1000 5y9rdffcnchk6 1 1
select count(*) from t1 where object_id between 999 and 60000 b8xxw70vja3tn 1 1
額外注意:
若是要在10.2.0.4版本中使用dbms_shared_pool.purge,則使用以前手工設置event 5614566(alter session set events ‘5614566 trace name context forever’),不然dbms_shared_pool.purge將不起做用,在其以上的版本就不存在這個限制了。
如今在進行測試修改以下:
SQL> exec :y := 60000;
PL/SQL 過程已成功完成。
SQL> select count(*) from t1 where object_id between :x and :y;
COUNT(*)
----------
58577
SQL> select sql_text,sql_id,version_count,executions from v$sqlarea where sql_text like 'select count(*) from t1%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
----------------------------------------------------------------- --------------- ------------- ----------
select count(*) from t1 where object_id between 999 and 1000 5y9rdffcnchk6 1 1
select count(*) from t1 where object_id between 999 and 60000 b8xxw70vja3tn 1 1
select count(*) from t1 where object_id between :x and :y 9dhu3xk2zu531 2 1
executions爲1證實確實執行的硬解析,可是version_count是2?應該是1纔對,以下查詢的child cursor確實只有一個,因此應該爲1,也許是Oracle bug致使。
SQL> select plan_hash_value,child_number from v$sql where
sql_id='9dhu3xk2zu531';
PLAN_HASH_VALUE CHILD_NUMBER
--------------- ------------
2351893609 0
再來看其執行計劃:
SQL> select * from table(dbms_xplan.display_cursor('9dhu3xk2zu531',0,
'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------
SQL_ID 9dhu3xk2zu531, child number 0
-------------------------------------
select count(*) from t1 where object_id between :x and :y
Plan hash value: 2351893609
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
|* 2 | FILTER | | | | | |
|* 3 | INDEX RANGE SCAN| IDX_T1 | 230 | 1150 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
3 - SEL$1 / T1@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
DB_VERSION('12.1.0.2')
OPT_PARAM('_optim_peek_user_binds' 'false')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(:Y>=:X)
3 - access("OBJECT_ID">=:X AND "OBJECT_ID"<=:Y)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT(*)[22]
已選擇 47 行。
選擇是仍是idx_t1的索引範圍掃描,意味着把綁定變量關掉之後,不管傳入值爲多少都不會影響Oracle對於目標SQL執行計劃的改變,而且其結果集從上述執行計劃看出不管傳入值爲多少,cardinality都爲230,以下來解釋如何計算的
計算公式以下:
Cardinality = NUM_ROWS * SelectivitySelectivity = 0.05 * 0.05
註釋:
1)、上述計算公司適用於禁用綁定變量窺探且where條件爲目標列between x and y的selectivity和cardinality計算
2)、num_rows表示目標列所在表的記錄數。
3)、where條件爲「目標列between x and y」,至關於「目標列 >= x and 目標列 <= y」。對於「目標列 >= x」和「目標列 <= y」而言,Oracle均會使用5%的可選擇率,因此「目標列 >= x and 目標列 <= y」總的可選擇率是0.05*0.05
將以前查詢的num_rows和0.05*0.05帶入公式,查看結果:
SQL> select round(91944*0.05*0.05) from dual; ROUND(91944*0.05*0.05)---------------------- 230