天天看點

淺談cursor_sharing 取值對SQL共享的影響

在Oracle中,使用者應用輸入的SQL語句要進行所謂的Parse解析過程,用于生成執行計劃,這也就是Query Optimizer的主要工作。在Parse中,有兩種具體類型,被稱為“hard parse”(硬解析)和“Soft parse”(軟解析)

“實作執行計劃shared cursor共享,減少硬解析”是我們OLTP系統優化一個重要方向。但是,讓Oracle真正實作SQL共享不是一件容易的事情,受到很多其他因素的影響。最常用的方式是使用綁定變量,讓SQL字面值保持一緻。如果應用端沒有使用綁定變量,一種做法是設定系統參數cursor_sharing,将SQL語句中的條件進行綁定變量替換。本篇将從cursor_sharing可選值含義入手,讨論分析幾種取值的确切含義和應用場景。以及為什麼很多資料中都是對cursor_sharing設定望而卻步

1.實驗環境

我們在Oracle 11g下準備一個相對偏值的資料表。

SQL> select * from v$version;

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0    Production

TNS for Linux: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 - Production

在Oracle 11g裡,預設cursor_sharing取值為EXACT,表示不開啟SQL字面取值綁定變量替換功能。

SQL> show parameter cursor_sharing

NAME                     TYPE     VALUE

------------------------------------ ----------- ------------------------------

cursor_sharing                 string     EXACT

使用腳本生成資料表資料

SQL> create table t (id1 varchar2(10), id2 varchar2(10), id3 varchar2(10));

Table created.

SQL> create index idx_t_id1 on t(id1);

Index created.

SQL> select object_id from dba_objects where owner='ZYSCM' and object_name='T';

 OBJECT_ID

----------

    170516

SQL> select id1, count(*) from t group by id1;

ID1         COUNT(*)

---------- ----------

P           11

D           22

A            7

SQL>

2、  統計量收集

這裡單獨談談統計量收集的問題。在Oracle統計量中,通常選擇直方圖histogram進行偏度描述。

注意:在Oracle 9i中,直方圖預設使用dbms_stats是不會收集的,需要手工的制定method_opts參數。在Oracle 10g之後,使用“column auto”作為method_opts參數的預設取值

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,method_opt => 'for all columns size auto');

PL/SQL procedure successfully completed.

SQL> select column_name, num_distinct, NUM_BUCKETS, HISTOGRAM from dba_tab_col_statistics where owner='ZYSCM' and table_name='T';

COLUMN_NAME               NUM_DISTINCT NUM_BUCKETS HISTOGRAM

------------------------------ ------------ ----------- ---------------

ID1                      3          1 NONE

ID2                      3          1 NONE

ID3                      3          1 NONE

SQL>

注意,預設是沒有生成直方圖的。主要原因在于需要使用一次id1作為條件列。

//使用一次條件列;

SQL> select count(*) from t where id1='D';

  COUNT(*)

----------

    22

//重新收集一下統計量;

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,method_opt => 'for all columns size auto');

PL/SQL procedure successfully completed.

//發現統計量收集

SQL> select column_name, num_distinct, NUM_BUCKETS, HISTOGRAM from dba_tab_col_statistics where owner='ZYSCM' and table_name='T';

COLUMN_NAME               NUM_DISTINCT NUM_BUCKETS HISTOGRAM

------------------------------ ------------ ----------- ---------------

ID1                      3          3 FREQUENCY

ID2                      3          1 NONE

ID3                      3          1 NONE

SQL>

當我們使用過一次id1條件之後,再次手動統計量,使用預設的auto參數,就生成id1列的頻度直方圖。

這裡也就揭示了Oracle在收集統計量直方圖auto選項的含義。當我們指定auto之後,Oracle會自動判斷是否對資料列生成直方圖、生成直方圖bullet的個數。如果這個列從來就沒有出現在SQL條件列中,也就不會被收集直方圖。

3、EXACT——不進行條件列替換

EXACT是cursor_sharing參數的預設選項,表示含義是不進行SQL條件自動綁定變量替換。

SQL> show parameter cursor_sharing

NAME                     TYPE     VALUE

------------------------------------ ----------- ------------------------------

cursor_sharing                 string     EXACT

SQL> alter system flush shared_pool;

System altered.

我們發出兩句SQL,分别使用資料取值差異很大的id1值。

SQL> select count(*) from t where id1='P';

  COUNT(*)

----------

    11

SQL> select count(*) from t where id1='A';

  COUNT(*)

----------

     7

此時,父子遊标library cache中情況如下:

SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select %';

SQL_TEXT                                                                  SQL_ID         VERSION_COUNT  EXECUTIONS

-------------                                                           -----------    -----------  ---------

select count(*) from t where id1='P'  1fu450v8ddmv9          1         1

select count(*) from t where id1='A'  16jfmsdnc6as0          1         1

在EXACT下,不會發生SQL字面值改寫的情況。如果兩個SQL的其他部分相同,隻是where條件的取值有差異,Oracle是會将這兩個語句作為兩個單獨SQL進行硬解析,分别生成執行計劃。

4、  FORCE——強制共享執行計劃

預設值EXACT的作用是不對非字面SQL綁定變量進行替換操作。而FORCE值和SIMILAR取值意味着Oracle需要對輸入的SQL語句進行處理,首先就是對條件值進行綁定變量化,其次就是針對不同的取值采用不同的執行計劃共享政策。

當選擇FORCE值的時候,意味着Oracle會對SQL字面值進行綁定變量處理。一個語句形成父遊标和僅有的一個子遊标。子遊标執行計劃通過Oracle binds peeking技術實作,以後所有類似形态的SQL都是先共享。

SQL> alter system flush shared_pool;

System altered.

SQL> alter session set cursor_sharing='FORCE';

Session altered.

SQL> show parameter cursor_sharing

NAME                     TYPE                  VALUE

------------------------------------ -------------------------------- ------------------------------

cursor_sharing                 string                  FORCE

将目前會話的cursor_sharing設定為force,同時清空library cache。之後使用三條SQL語句進行試驗。

SQL> select count(*) from t where id1='D';

  COUNT(*)

----------

    22

SQL>   select count(*) from t where id1='P';

  COUNT(*)

----------

    11

SQL>  select count(*) from t where id1='A';

  COUNT(*)

----------

     7

如果在EXACT取值的時候,三個執行語句一定會生成三個父遊标和三個子遊标的。每一個遊标對應一個單獨的執行計劃。第一和第二條SQL對應全表掃描FTS方案較好,而第三條SQL顯然索引路徑較優。我們看看在FORCE取值的時候,生成計劃情況如何呢?

SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like '%%';

SQL_TEXT                                                                           SQL_ID          VERSION_COUNT EXECUTIONS

--------------------------------------------------------------------------------  ------------- ------------- ----------

 select count(*) from t where id1=:"SYS_B_0"      2yczsvd6tcjuj    1            3

此時,我們觀察到三次執行之後SQL遊标共享情況。首先,三次的SQL語句從字面值上完全不同,差異隻是存在在條件id1取值上。如果在cursor_sharing為EXACT模式下,是不能實作遊标共享的。設定為FORCE之後,我們發現Oracle自動将id1=後面的條件替換為綁定變量。三次SQL調用均使用相同的父遊标,而子遊标隻存在一個,意味著三次調用均是使用這個唯一的子遊标。一個子遊标對應一個執行計劃,三個SQL使用相同的執行計劃。

5、SIMILAR——另一個極端

剛剛我們讨論了FORCE。在FORCE下,問題是很簡單的:進行綁定變量替換,共享全部遊标。但是這樣對于資料分布不均衡的條件列來說,是存在很多問題的。實際環境中會出現SQL性能時好時壞的情況。作為另一個極端,我們設定SIMILAR取值。

SQL> alter system set cursor_sharing='SIMILAR';

System altered.

SQL> show parameter cursor_sharing;

NAME                     TYPE

------------------------------------ --------------------------------

VALUE

------------------------------

cursor_sharing                 string

SIMILAR

為了更容易看清現象,我們使用逐漸試驗的方法:

--Invoke SQL 1

SQL> select count(*) from t where id1='P';

  COUNT(*)

----------

    11

SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like '%%';

SQL_TEXT

--------------------------------------------------------------------------------

SQL_ID          VERSION_COUNT EXECUTIONS

------------- ------------- ----------

select count(*) from t where id1=:"SYS_B_0"

f0rpwa1ja706p  1         1

第一次調用SQL語句,使用條件值D。在library cache中生成了父子遊标,而且同FORCE一樣,進行了綁定變量替換。下面進行第二次調用:

SQL>  select count(*) from t where id1='A';

  COUNT(*)

----------

     7

SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like '%%';

SQL_TEXT                                                                              SQL_ID          VERSION_COUNT EXECUTIONS

--------------------------------------------------------------------------------     ------------- ------------- ----------

select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text     f0rpwa1ja706p      2              2

 like '%%'

在使用一個新值A的情況下,生成了一個新的子遊标(version_count=2)

6、結論

cursor_sharing的取值和參數是Oracle library cache中管理生成乃至共享執行計劃的重要參數。EXACT值是預設值,實作了直接使用字面SQL不開啟轉變綁定變量的功能。

而FORCE和SIMILAR取值卻開啟了字面轉綁定變量的功能。在這兩個模式下,Oracle會自動的将where後面的條件替換為綁定變量,以增加SQL共享的機率。具體實作sharing的方式上,FORCE和SIMILAR取值又有所差異。

FORCE的sharing原則是共享一切,隻生成一個子遊标,之後所有都去共享這個子遊标的執行計劃。随之而來的就是bind peeking問題風險。

而SIMILAR過于謹慎,對每一個SQL都進行類似bind peeking操作。對每個可能取值都生成單獨的子遊标執行計劃。相同的輸入共享相同的執行計劃。這個雖然避免了bind peeking問題,卻帶來了新的多version count問題。

從EXACT到FORCE到SIMIlAR,到Oracle 11g中推出的ACS(Adaptive Cursor Sharing),Oracle一直試圖去實作cursor sharing的自動化和高效化。過去,隻能通過手工顯示綁定變量來實作SQL共享最大化的目标。而手工書寫的大部分SQL由于字面值的原因很難共享。cursor_sharing參數的作用就是進行這方面的嘗試,雖然從目前看還是有一些問題,但是已經進行了有益的嘗試。

Oracle 11g中推出的ACS自适應遊标,将遊标共享的标準從SQL字面值相同,綁定變量Peeking值相同,拓展到執行計劃相同。在不斷的自适應嘗試過程中,Oracle ACS最終會确定适合的共享方案和執行計劃。

繼續閱讀