1. 程式人生 > >關於繫結變數關閉的情況,Oracle是如何工作的?

關於繫結變數關閉的情況,Oracle是如何工作的?

關於如果繫結變數窺探被關閉了,oracle 會怎麼處理的呢?是每次都硬解析還是這樣處理?

首先介紹下繫結變數窺探:
使用SQL首次執行時的值來生成執行計劃。後續再次執行該SQL語句則使用首次執行計劃來執行。
測試版本:Oracle 12.1.0.2
對於繫結變數列中的特殊值或非均勻分佈列上的繫結變數會造成非高效的執行計劃被選擇並執行。要注意的是,Bind Peeking只發生在硬分析的時候,即SQL被第一次執行的時候,之後的變數將不會在做peeking。我們可以看出,Bind peeking並不能最終解決不同謂詞導致選擇不同執行計劃的問題,它只能讓SQL第一次執行的時候,執行計劃選擇更加準確,在第一次解析SQL時,按照窺探變數的值生成執行計劃,以後這樣的SQL都按照這個執行。隱藏引數_optim_peek_user_binds=true則啟用繫結變數窺探,否則CBO認為統計列是均勻的。
然後就是對這個問題分析處理思路如下:
如果繫結變數窺探關閉了,那麼是否有直方圖對執行計劃就影響不大了,因為oracle都把它作為一個引數帶入,而不會去檢視他的值,
連第一次都不檢視。如果不窺探,那麼按照正常的值進行成本分析,然後oracle不考慮具體的值,把這個值作為一個均勻分佈帶入。
如果窺探了,僅僅是第一次會去窺探,因此,如果每次都窺探,就變成硬解析了,
硬解析對oltp有多大的危害,不覺明歷:
下面用簡單的實驗說明下這個問題:

  • 結論1

: 繫結變數窺探一定要有直方圖的時候才會起效果,否則一樣窺探沒效果。
建立一張測試表,並建立一個索引,然後插入一批50000行object_id為0的資料,然後收集一下表的統計資訊,然而並不收集它的直方圖資訊,操作如下:

SQL> create table objt as select object_id,object_name from t_objects;
表已建立。
SQL> create index idx_obj_id on objt(object_id);
索引已建立。
SQL> declare
  2  begin
  3     for i in
1..50000 loop 4 insert into objt values(0,i); 5 end loop; 6 end; 7 / PL/SQL 過程已成功完成。 SQL> SQL> SQL> SQL> select count(*) from objt; COUNT(*) ---------- 104960 SQL> select count(*) from objt where object_id=0; COUNT(*) ---------- 50000 SQL> BEGIN 2 DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST'
, 3 tabname => 'OBJT', 4 estimate_percent => 100, 5 method_opt => 'for all columns size 1', 6 degree => DBMS_STATS.AUTO_DEGREE, 7 cascade=>TRUE 8 ); 9 END; 10 / PL/SQL 過程已成功完成。 SQL> SELECT TABLE_NAME,column_name,num_distinct,histogram from dba_tab_col_statistics where table_name='OBJT' and column_name='OBJECT_ID'; TABLE_NAME -------------------------------------------------------------------------------- COLUMN_NAME -------------------------------------------------------------------------------- NUM_DISTINCT HISTOGRAM ------------ ------------------------------ OBJT OBJECT_ID 54961 NONE

可以看到,表objt對應的列上是沒有直方圖統計資訊的。
然後執行一個賦值查詢如下:

SQL> var a number;
SQL> execute :a := 0;
PL/SQL 過程已成功完成。
SQL> set linesi 200
SQL> select count(object_name) from objt where object_id=:a;
COUNT(OBJECT_NAME)
------------------
             50000
SQL> select * from table(dbms_xplan.display_cursor());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  fmsznp4v247sc, child number 0
-------------------------------------
select count(object_name) from objt where object_id=:a
Plan hash value: 909060252
---------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |            |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE                      |            |     1 |    19 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| OBJT       |     2 |    38 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | IDX_OBJ_ID |     2 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("OBJECT_ID"=:A)
已選擇 20 行。

雖然0值佔了一半的資料,但是CBO依然走了索引範圍掃描。這裡是不是感覺很不合理,應該全表掃描才對。主要原因是雖然使用了繫結變數窺探,但是由於沒有直方圖,即使窺探了,也是採用均勻分佈分析,走索引的執行計劃。
然後再次收集表的統計資訊,並且收集object_id列上的直方圖資訊。

SQL> BEGIN
  2     DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST',
  3                                   tabname => 'OBJT',
  4                                   estimate_percent => 100,
  5                                   method_opt => 'for all columns size auto',
  6                                   degree => DBMS_STATS.AUTO_DEGREE,
  7                                   cascade=>TRUE
  8                                   );
  9  END;
 10  /
PL/SQL 過程已成功完成。
SQL> SELECT TABLE_NAME,column_name,num_distinct,histogram from dba_tab_col_statistics where table_name='OBJT' and column_name='OBJECT_ID';
TABLE_NAME
-------------------------
COLUMN_NAME
--------------------------
NUM_DISTINCT HISTOGRAM
------------ ------------------------------
OBJT
OBJECT_ID
       54961 HEIGHT BALANCED
SQL> var b number;
SQL> execute :b := 0;
PL/SQL 過程已成功完成。
SQL> select count(object_name) from objt where object_id=:b;
COUNT(OBJECT_NAME)
------------------
             50000
SQL> select * from table(dbms_xplan.display_cursor());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  4k53s1bkc30ph, child number 0
-------------------------------------
select count(object_name) from objt where object_id=:b
Plan hash value: 4162353065
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   106 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    19 |            |          |
|*  2 |   TABLE ACCESS FULL| OBJT | 49587 |   920K|   106   (1)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("OBJECT_ID"=:B)
已選擇 19 行。

再次檢視,確實走了全表掃描,這就是繫結變數窺探的作用就體現了出來。

  • 結論2

:關閉繫結變數窺探,以後每次也是軟解析,而且關閉了繫結變數窺探之後,即使你分析了直方圖還是執行均勻分佈,按照cost計算出成本,然後走oracle認為最優的執行計劃:

SQL> set linesi 200
SQL> col name for a50
SQL> col value for a20
SQL> select
  2  x.ksppinm name,
  3  y.ksppstvl value,
  4  y.ksppstdf isdefault,
  5  decode(bitand(y.ksppstvf,7),1,'MODIFIED',4,'SYSTEM_MOD','FALSE') ismod,
  6  decode(bitand(y.ksppstvf,2),2,'TRUE','FALSE') isadj
  7  from
  8  sys.x$ksppi x,
  9  sys.x$ksppcv y
 10  where
 11  x.inst_id = userenv('Instance') and
 12  y.inst_id = userenv('Instance') and
 13  x.indx = y.indx
 14  and x.ksppinm like '%_optim_peek_user_binds%'
 15  order by
 16  translate(x.ksppinm, ' _', ' ');
NAME                                               VALUE                ISDEFAULT          ISMOD                ISADJ
-------------------------------------------------- -------------------- ------------------ -------------------- ----------
_optim_peek_user_binds                             TRUE                 TRUE               FALSE                FALSE
SQL> alter session set "_optim_peek_user_binds"=false;
會話已更改。
SQL> var d number;
SQL> exec :d := 0;
PL/SQL 過程已成功完成。
SQL> select count(object_name) from objt where object_id = :d;
COUNT(OBJECT_NAME)
------------------
             50000
SQL> col value for 99999999
SQL> select a.value,b.value from v$sysstat a,v$sysstat b where a.name='parse count (hard)' and b.name='parse count (total)';
    VALUE     VALUE
--------- ---------
    14186    184689
SQL> select count(object_name) from objt where object_id = :d;
COUNT(OBJECT_NAME)
------------------
             50000
SQL> select a.value,b.value from v$sysstat a,v$sysstat b where a.name='parse count (hard)' and b.name='parse count (total)';
    VALUE     VALUE
--------- ---------
    14186    184691 -----可以看到硬解析已經不再發生變化,但是total解析數一直在隨著查詢的增加而增加即為軟解析或軟軟解析
SQL> SELECT TABLE_NAME,column_name,num_distinct,histogram from dba_tab_col_statistics where table_name='OBJT' and column_name='OBJECT_ID';
TABLE_NAME
-------------------------------
COLUMN_NAME
-------------------------------
NUM_DISTINCT HISTOGRAM
------------ ------------------------------
OBJT
OBJECT_ID
       54961 HEIGHT BALANCED  ----這裡是有直方圖的
SQL> select count(object_name) from objt where object_id = :d;
COUNT(OBJECT_NAME)
------------------
             50000
SQL> select * from table(dbms_xplan.display_cursor());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  d56agappx6ds9, child number 0
-------------------------------------
select count(object_name) from objt where object_id = :d
Plan hash value: 909060252
---------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |            |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE                      |            |     1 |    19 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| OBJT       |     2 |    38 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | IDX_OBJ_ID |     2 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("OBJECT_ID"=:D)
已選擇 20 行。

可以沒有,即使是一半數量的0,還是走索引操作,可見這個時候的繫結變數窺探功能關閉了,Oracle把這個0當成是帶入引數了並且採用所認為的資料分佈均衡的方式來估算的。

  • 總結:

繫結變數窺探功能要在有直方圖的列分析裡面才能起到第一次的窺探作用,但是如果你是傾斜列,也會導致執行計劃走錯,關閉了繫結變數窺探功能,會導致連第一次窺探都沒有,後續所有的sql依然會軟解析。