如何進(jìn)行 Adaptive Cursor Sharing的研究,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
成都創(chuàng)新互聯(lián)專注于同心網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供同心營銷型網(wǎng)站建設(shè),同心網(wǎng)站制作、同心網(wǎng)頁設(shè)計(jì)、同心網(wǎng)站官網(wǎng)定制、微信平臺小程序開發(fā)服務(wù),打造同心網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供同心網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
Adaptive Cursor Sharing(ACS)是又一個大膽而吸引人的11G新特性。
說它大膽是因?yàn)樗噲D解決一個CBO最令人頭疼的問題:數(shù)據(jù)傾斜(data skew)和綁定變量窺視導(dǎo)致SQL PLAN太差。說它吸引人是因?yàn)橄胫繭racle采用何種神秘的算法讓Oracle變得更加智能。
之所以在11GR2出來之后才開始研究,是因?yàn)檫@個new feture在11GR1時有各種各樣的問題,首先映入我眼簾的就是這個bug:
Bug 7213010 Adaptive cursor sharing generates lots of child cursors
This issue is fixed in 11.2 (Future Release),11.1.0.7 (Server Patch Set)
ACS其實(shí)就是根據(jù)不同綁定變量的值為同一個SQL生成更多更優(yōu)的執(zhí)行計(jì)劃,來適應(yīng)data skew的不同情況。正因?yàn)槿绱?,才會有如上的bug出現(xiàn)。
<一> 我們先用一個簡單的例子來走近ACS。
(注意:以下實(shí)驗(yàn)的SQL PLAN的獲取不能信任set autotrace,他不會顯示各個child cursor的實(shí)際執(zhí)行計(jì)劃,我們可以通過SELECT * FROM table (DBMS_XPLAN.DISPLAY_CURSOR(:sqlid, NULL, 'TYPICAL LAST'))來得到真實(shí)的PLAN。)
新建一個兩個字段的表,其中id這列十分傾斜,并在id這列上創(chuàng)建index,并使用SKEWONLY選項(xiàng)分析表,使其生成histogram。
為了不讓其他因素干擾我的實(shí)驗(yàn)并且讓讀者能夠重現(xiàn),我設(shè)置如下參數(shù):
optimizer_mode=CHOOSE(使用CBO)
optimizer_features_enable=11.2.0.1(使用最新的優(yōu)化參數(shù))
optimizer_capture_sql_plan_baselines=false(關(guān)閉SPM)
cursor_sharing=EXACT(使用真正的綁定變量)
_optim_peek_user_binds=true(一定要開啟綁定變量窺視)
_optimizer_adaptive_cursor_sharing=TRUE(以下三個參數(shù)默認(rèn)開啟ACS)
_optimizer_extended_cursor_sharing=UDO
_optimizer_extended_cursor_sharing_rel=SIMPLE
SQL> desc testbyhao
Name Type
----- --------
ID NUMBER
NAME VARCHAR2(128)
SQL> select id,count(*) from testbyhao
group by id;
ID COUNT(*)
---------- ----------
1 104096
2 498
SQL> create index testidx on testbyhao(id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'TESTBYHAO',
method_opt=>'for all columns size skewonly');
PL/SQL procedure successfully completed.
SQL> select COLUMN_NAME,HISTOGRAM from user_tab_columns
where TABLE_NAME='TESTBYHAO';
COLUMN_NAM HISTOGRAM
---------- ------------------------------
ID FREQUENCY
NAME HEIGHT BALANCED
先生成一個最簡單的執(zhí)行計(jì)劃index range scan。
對于id=2來說,是相當(dāng)合適的。
SQL> var v number;
SQL> exec :v :=2;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
Plan hash value: 254123311
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TESTBYHAO | 387 | 8127 | 3 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | TESTIDX | 387 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=:V)
從v$SQL中,可以看到這個cursor的數(shù)據(jù),其中IS_BIND_SENSITIVE=Y,表明使用綁定變量窺視來生成這次執(zhí)行計(jì)劃,這次執(zhí)行計(jì)劃是取決于這個綁定變量的,如果Oracle發(fā)現(xiàn)有其他的綁定變量出現(xiàn),是可能生成其他的執(zhí)行計(jì)劃的。
IS_BIND_AWARE=N,表明Oracle還沒有使用extended cursor sharing。
IS_SHAREABLE=Y,表明這個cursor可以被再次使用,即能夠共享;反之,設(shè)為N代表著這個cursor已經(jīng)過時了,不會被再用了,這個cursor將會等待被age out出shared pool。
SQL> select CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
BUFFER_GETS/EXECUTIONS BG_PER_EX,
IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
from v$sql where hash_value=1659091011;
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 1 221 Y N Y
更換綁定變量,使用id=1執(zhí)行同樣的SQL.
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
結(jié)果,使用綁定變量id=1的SQL使用了同樣的index range scan的cursor。這其實(shí)不是我們希望的,因?yàn)閕d=1時明顯走全表掃描cost更低。
v$SQL沒怎么變,只是同樣的cursor執(zhí)行次數(shù)為2了。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N Y
再次執(zhí)行同樣的id=1的SQL。
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
Plan hash value: 325910803
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 109 (100)| |
|* 1 | TABLE ACCESS FULL| TESTBYHAO | 104K| 2136K| 109 (4)| 00:00:02 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"=:V)
終于,我們期望的事情發(fā)生了,新的全表掃描的執(zhí)行計(jì)劃產(chǎn)生了?。▽?yīng)于CHILD_NUMBER=1,PLAN_HASH_VALUE=325910803)
v$SQL里,新的cursor的IS_BIND_AWARE=Y。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N Y
1 325910803 1 7296 Y Y Y
再次執(zhí)行id=1的SQL
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
CHILD 1執(zhí)行次數(shù)增加為2
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N Y
1 325910803 2 7296 Y Y Y
再次執(zhí)行id=2的SQL
SQL> exec :v := 2;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
奇怪的事情發(fā)生了,又新生成了一個index range scan的cursor(CHILD 2),并且CHILD 0的IS_SHAREABLE=N了,表明這個cursor不再被使用了。
我想這是因?yàn)镺racle會監(jiān)控每個cursor的平均selectivity,當(dāng)新進(jìn)來的綁定變量的cursor跟現(xiàn)有的cursor都差得比較遠(yuǎn)時,就會新生成一個cursor,即使他們的執(zhí)行計(jì)劃是有可能一樣的。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 2 7296 Y Y Y
2 254123311 1 73 Y Y Y
再次執(zhí)行id=2的SQL
SQL> exec :v := 2;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
CHILD 2 執(zhí)行次數(shù)增加為2
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 2 7296 Y Y Y
2 254123311 2 73 Y Y Y
更換綁定變量id=999,再次執(zhí)行。
SQL> exec :v := 999;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
果然,新的cursor CHILD 3出生了,雖然他依然使用的是index range scan,但它的selectivity是0。
這時,CHILD 2又“死”了。(IS_SHAREABLE=N)
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 2 7296 Y Y Y
2 254123311 2 73 Y Y N
3 254123311 1 2 Y Y Y
使用id=2再來試試。
SQL> exec :v := 2;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
使用了新的CHILD 3的cursor。
SQL> /
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 2 7296 Y Y Y
2 254123311 2 73 Y Y N
3 254123311 2 37.5 Y Y Y
再換個綁定變量id=111試試。
SQL> exec :v := 111;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
依然使用了CHILD 3,看來現(xiàn)在執(zhí)行計(jì)劃基本處于一種穩(wěn)定的狀態(tài)了。
SQL> /
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 2 7296 Y Y Y
2 254123311 2 73 Y Y N
3 254123311 3 25.6666667 Y Y Y
再使用id=1來試試
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
果然,CHILD 1被使用。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7321.5 Y N N
1 325910803 3 7296 Y Y Y
2 254123311 2 73 Y Y N
3 254123311 3 25.6666667 Y Y Y
<二> 接著,我們來關(guān)注一下三個ACS的視圖。
1.v$sql_cs_histogram
SQL> SELECT CHILD_NUMBER,BUCKET_ID,COUNT FROM v$sql_cs_histogram
2 WHERE HASH_VALUE = '1659091011' order by 1,2,3;
CHILD_NUMBER BUCKET_ID COUNT
------------ ---------- ----------
0 0 1
0 1 1
0 2 0
1 0 0
1 1 3
1 2 0
2 0 2
2 1 0
2 2 0
3 0 3
3 1 0
3 2 0
這個視圖對于每個Child Cursor有三個buckets,用來計(jì)算每個cursor被執(zhí)行的次數(shù)。
2.v$sql_cs_selectivity
SQL> l
1 SELECT CHILD_NUMBER,PREDICATE,RANGE_ID,LOW,HIGH FROM
2* v$sql_cs_selectivity WHERE HASH_VALUE = '1659091011'
SQL> /
CHILD_NUMBER PREDICATE RANGE_ID LOW HIGH
------------ ---------- ---------- ------------------------------ ------------------------------
3 =V 0 0.001705 0.004070
2 =V 0 0.003330 0.004070
1 =V 0 0.896589 1.095831
這個視圖顯示對于每種Child Cursor,它最高和最低的selectivity是多少。
這是因?yàn)镺racle不會也不可能對每個綁定變量都產(chǎn)生一個Child Cursor,那么不同綁定變量就得根據(jù)自身的selectivity來在已有的Child Cursor中尋找,是否有比較接近的選擇率,如果有,那么就重用這個cursor;否則,就如前面我的實(shí)驗(yàn)一樣,新的Child Cursor就會孕育而生。
3.v$sql_cs_statistics
SQL> SELECT CHILD_NUMBER,BIND_SET_HASH_VALUE,PEEKED,EXECUTIONS,
2 ROWS_PROCESSED,BUFFER_GETS,CPU_TIME
3 FROM v$sql_cs_statistics WHERE HASH_VALUE = '1659091011';
CHILD_NUMBER BIND_SET_HASH_VALUE PEE EXECUTIONS ROWS_PROCESSED BUFFER_GETS CPU_TIME
------------ ------------------- --- ---------- -------------- ----------- ----------
3 3028748107 Y 1 0 2 0
2 2064090006 Y 1 996 73 0
1 2342552567 Y 1 104096 7296 0
0 2064090006 Y 1 996 221 0
這個視圖故名思意,顯示的是各個Child Cursor的統(tǒng)計(jì)信息,例如是不是使用了綁定變量窺視,返回行數(shù)有多少,邏輯IO有多少等等。
如果需要查看到底是什么綁定變量產(chǎn)生的這些cursor,可以使用如下SQL查詢v$sql_bind_capture:
SQL> SELECT CHILD_NUMBER,VALUE_STRING,LAST_CAPTURED
2 FROM v$sql_bind_capture WHERE HASH_VALUE = '1659091011' order by 1;
CHILD_NUMBER VALUE_STRI LAST_CAPTURED
------------ ---------- -----------------
0 2 20091203 05:37:11
1 1 20091203 05:39:23
2 2 20091203 05:42:18
3 999 20091203 05:43:15
<三> 如何關(guān)閉ACS?
alter system set "_optimizer_extended_cursor_sharing_rel"=none;
alter system set "_optimizer_extended_cursor_sharing"=none;
alter system set "_optimizer_adaptive_cursor_sharing"=false;
證明:
SQL> alter session set "_optimizer_extended_cursor_sharing_rel"=none;
SQL> alter session set "_optimizer_extended_cursor_sharing"=none;
SQL> alter session set "_optimizer_adaptive_cursor_sharing"=false;
SQL> alter system flush shared_pool;
SQL> var v number;
SQL> exec :v :=2;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 1 286 N N Y
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 2 7354 N N Y
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 3 9710 N N Y
可見,當(dāng)我在session級別關(guān)閉這三個隱藏參數(shù)后,IS_BIND_SENSITIVE始終為N,更換綁定變量后也不會產(chǎn)生新的cursor。
所以,當(dāng)我們再做11GR2升級時,可以先考慮關(guān)閉這三個參數(shù)謀求SQL PLAN的穩(wěn)定的同時,使用其他11G的new feature。
畢竟對于高并發(fā)的OLTP數(shù)據(jù)庫,穩(wěn)定重于一切。
<四>使用hint強(qiáng)制BIND_AWARE
在我研究11G所有新hint時,發(fā)現(xiàn)了這個hint:BIND_AWARE。
于是,就有了研究ACS的沖動,才有了這篇文章。
這個hint故名思意,會強(qiáng)制SQL產(chǎn)生BIND_AWARE的cursor。
更加強(qiáng)悍的是,即使你如上面第三點(diǎn)所示關(guān)閉了這三個ACS的參數(shù),但hint依舊生效!
我先如上在session級別關(guān)閉這三個ACS的參數(shù),然后進(jìn)行了如下實(shí)驗(yàn)。
SQL> exec :v := 1;
SQL> select /*comments*/ * from TESTBYHAO
2 where id = :v;
我們先發(fā)現(xiàn),IS_BIND_AWARE=N。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 1 7515 N N Y
接著加上BIND_AWARE這個hint:
SQL> select /*+BIND_AWARE*/ * from TESTBYHAO
2 where id = :v;
可以看見區(qū)別了吧,IS_BIND_SENSITIVE=Y,IS_BIND_AWARE=Y。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 1 7296 Y Y Y
接下來更換綁定變量再run幾次,結(jié)果就是我們所熟悉的了。
再重申下,這是在我關(guān)閉session級別的ACS參數(shù)后進(jìn)行的測試。
可見,BIND_AWARE這個hint很強(qiáng)悍。
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 2 7296 Y Y Y
1 254123311 2 73 Y Y N
2 254123311 1 2 Y Y Y
于是,我想到了這樣的假設(shè)的情況,當(dāng)我們升級到11GR2后,由于對ACS不了解,不敢用,于是再系統(tǒng)級別關(guān)閉了ACS的三個參數(shù)。但是突然某一天,我發(fā)現(xiàn)了一個由于data skew并且采用綁定變量的SQL PLAN不好調(diào)整時,我可以讓開發(fā)人員對這個特定的SQL加上這個hint,讓其突破關(guān)閉ACS的限制,使用ACS。于是,這似乎可以成為新的SQL tunning的好方法。
<五>萬能的outline強(qiáng)于一切
本來寫完前四點(diǎn)就想結(jié)束了,但突然想到我們現(xiàn)有的系統(tǒng)上使用了無數(shù)的outline來固定SQL PLAN。那么如果升級到11GR2后,在ACS的強(qiáng)大統(tǒng)治力下,outline會不會失效呢?
帶著這個疑問,我做了如下的實(shí)驗(yàn),結(jié)論是:outline強(qiáng)于一切!甚至可以突破BIND_AWARE這個強(qiáng)有力的hint的限制!
實(shí)驗(yàn)一:不使用BIND_AWARE這個hint
SQL> select /*comment*/ * from TESTBYHAO
2 where id = :v;
先如愿產(chǎn)生一個ACS會生效的cursor:
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 1 7519 Y N Y
使用outline固定這個SQL。
alter session set current_schema=HAOZHU_USER;
create outline ol_4107335673 for category temp_plan on
select /*comment*/ * from TESTBYHAO
where id = :v ;
alter session set current_schema=HAOZHU_USER;
create outline ol_temp4107335673 for category temp_plan on
select /*+full(TESTBYHAO)*/ /*comment*/ * from TESTBYHAO
where id = :v;
再exchange這兩個outline。
接著換id=2再次執(zhí)行同樣的SQL:
SQL> exec :v:=2
SQL> select /*comment*/ * from TESTBYHAO
2 where id = :v ;
結(jié)果v$SQL里產(chǎn)生了一個新的cursor(新的HASH_VALUE),并不是先前的cursor了,也不是先前的Child Cursor。
再多次執(zhí)行上面的id=2的同樣的SQL后,我們可以看到:
SQL> /
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 4 461.75 N N Y
可見,使用outline后,即使你開啟了ACS,ACS也不生效!
實(shí)驗(yàn)二:使用BIND_AWARE hint
接著有人會問,你第四點(diǎn)提到的BIND_AWARE這個hint這么強(qiáng)大,能夠突破關(guān)閉ACS的限制,那么能否突破outline的限制呢?
帶著這個疑問,我做了如下實(shí)驗(yàn):
先使用BIND_AWARE hint,走index range scan:
SQL> select /*+BIND_AWARE*/ /*comment*/ * from TESTBYHAO
2 where id = :v;
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 254123311 1 73 Y Y Y
如上,可見三個“Y”看著十分地舒服。
我接著創(chuàng)建outline固定使用全表掃描而不走index。
alter session set current_schema=HAOZHU_USER;
create outline ol_new for category temp_plan on
select /*+BIND_AWARE*/ /*comment*/ * from TESTBYHAO
where id = :v;
alter session set current_schema=HAOZHU_USER;
create outline ol_tempnew for category temp_plan on
select /*+FULL(TESTBYHAO)*/ /*+BIND_AWARE*/ /*comment*/ * from TESTBYHAO
where id = :v;
exchange這兩個outline。
然后再run一模一樣的這個SQL:
SQL> /
498 rows selected.
Note
-----
- outline "OL_NEW" used for this statement
接著看v$SQL里:
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX BS BA S
------------ --------------- ---------- ---------- --- --- ---
0 325910803 1 584 N N Y
果然,新的執(zhí)行計(jì)劃出現(xiàn)了,代替了原來的那個執(zhí)行計(jì)劃。
執(zhí)行次數(shù)還是1,意味者前面的cursor被flush出去了,這是一個嶄新的cursor。
IS_BIND_AWARE=N,IS_BIND_SENSITIVE=N意味著這個SQL不受ACS控制了!
結(jié)語:本來對于ACS這個有趣的特性還有無數(shù)的實(shí)驗(yàn)要做。比如我心里還有十萬個為什么:SQL profile受不受ACS控制?那三個ACS的隱藏參數(shù)每個的具體作用是什么?ACS的選擇率是如何計(jì)算出來的?為何有大于1的選擇率。。。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。