基於ORACLE SQL優化之綁定變量(2)

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

如今把隱含參數_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
相關文章
相關標籤/搜索