執行計劃管理
1. 執行計劃管理的工作原理
我們知道,SQL語句的性能很大程度上依賴于SQL語句的執行計劃。如果SQL語句的執行計劃發生改變,則SQL的性能很有可能發生大的變化。而SQL執行計劃發生變化的原因有很多種,包括優化器版本的變化、統計信息的變化、優化器相關的各種參數的變化、添加或刪除了索引、添加或刪除了物化視圖、修改了系統設置、以及是否應用了10g引入的SQL profile等。
在11g之前,我們可以使用存儲大綱(stored outline)和SQL Profile來幫助我們固定某條SQL語句的執行計劃,防止由于執行計劃發生變化而導致的性能下降。不過這些技術需要DBA人為的處理,比如存儲大綱,需要DBA手工創建,而SQL Profile來說,則要DBA手工應用才能生效。
從11g開始,oracle引入了SQL執行計劃管理(SQL Plan Management)這個新特性,從而可以讓系統自動的來控制SQL語句執行計劃的穩定性,進而防止由于執行計劃發生變化而導致的性能下降。
通過啟用該特性,某條語句如果產生了一個新的執行計劃,只有在它的性能比原來的執行計劃好的情況下,才會被使用。為了實現執行計劃管理,優化器會為所有執行次數超過一次的SQL語句維護該SQL語句的每個執行計劃的歷史列表(plan history)。優化器通過維護一個語句執行的日志條目(statement log)來識別該SQL語句是否為第二次執行。一旦優化器認出某條SQL語句為第二次執行,則優化器將該語句所生成的所有不同的執行計劃插入到plan history的相關表里,而plan history里包含了優化器能夠用于重新生成執行計劃的所有信息,這些信息包括SQL文本、存儲大綱、綁定變量以及解析環境(比如optimizer_mode之類影響優化器解析SQL語句的參數)等。
當然,11g也支持手工維護SQL語句的plan history,作為對自動維護plan history的功能補充。但是并不是說plan history里任何的執行計劃都可以拿來使用。這里需要先介紹一下所謂的執行計劃基準線(plan baseline)這個概念。plan baseline是plan history的一個子集,plan baseline里面的執行計劃是用來比較性能好壞的一個依據。也就是說,憑什么來判斷是否可以使用一個新產生的執行計劃呢?就是把該新的執行計劃與plan baseline里的計劃進行比較來判斷。某個SQL語句的執行計劃可以屬于plan history,但是不一定屬于plan baseline。
注意:每個SQL語句都會有它自己的執行計劃歷史以及執行計劃基準線。
那么某個SQL語句的執行計劃是如何進入執行計劃歷史,乃至執行計劃基準線的呢?
有兩種方法可以將SQL語句的執行計劃納入到執行計劃管理體系中去:
將初始化參數:OPTIMIZER_CAPTURE_PLAN_BASELINES設置為true,則會自動的捕獲SQL的執行計劃。該參數缺省為false。設置為true以后,當某條SQL語句第一次執行時,該SQL語句的plan history是空的,顯然該SQL語句的plan baseline也是空的。那么當該SQL第二次再次執行時,優化器會自動將該SQL語句以及相關的執行計劃放入plan history,同時也會放入到plan baseline里。這就構成了最初的plan baseline,也就是說最初進入plan history的執行計劃會直接自動進入plan baseline里。那么當你做了某些修改,比如添加了一個索引等,然后第三次執行該同樣的SQL語句,則會產生另外一個不同的執行計劃,這時新的執行計劃會自動進入plan history,但是不會自動進入plan baseline。是否使用該新的執行計劃,則要把該新的執行計劃與plan baseline里現存的第二次執行SQL時的執行計劃進行比較,如果新的執行計劃成本更低,則會使用新的執行計劃,否則使用plan baseline里的執行計劃。
使用dbms_spm包手工處理,這可以讓你手工管理SQL plan baseline。使用該包,你可以直接將SQL的執行計劃從shared pool里加載到plan baseline里,也可以使用dbms_spm包將已經存在的SQL Tuning Set加載到plan baseline里。同時,dbms_spm可以讓你把plan history里的執行計劃加入到plan baseline里。反之,你也可以使用該包將plan baseline里的執行計劃移出去。
注意,某條SQL語句的plan baseline里的第一個執行計劃可以像上面說的通過設置初始化參數來自動加入,但是如果你希望在plan baseline里添加該SQL語句的其他新的執行計劃時,則必須使用dbms_spm包手動完成。
那么plan baseline里的執行計劃是如何被使用的呢?
Oracle提供了一個初始化參數:OPTIMIZER_USE_PLAN_BASELINES,該參數缺省為true,表示要求優化器考慮使用plan baseline里的執行計劃,如果設置為false,則不使用執行計劃管理的特性,而又回到了11g之前的狀況。
以下描述基于的前提是plan baseline里已經存在了一個SQL的執行計劃。
每次優化器解析SQL語句的時候,首先仍然使用11g之前的傳統方式產生一個成本最低的執行計劃,然后看初始化參數:OPTIMIZER_USE_PLAN_BASELINES是否設置為true,如果為false,則直接返回所生成的執行計劃。否則如果為ture,則去plan history里找是否存在相同的執行計劃,如果找到了,則再去plan baseline里找是否存在相同的執行計劃,如果也找到了,則直接返回該執行計劃。如果在plan history里沒找到相同的執行計劃,則將產生的執行計劃加入到plan history里,然后將執行計劃與plan baseline里已經存在的執行計劃進行比較,看哪個執行計劃的成本低就取哪個執行計劃。
如果你為某個SQL語句保存了存儲大綱,則為了向下兼容,該語句會使用存儲大綱。另外,即使你通過設置初始化參數:OPTIMIZER_CAPTURE_PLAN_BASELINES為true來啟動自動捕獲執行計劃到plan baseline里,對于使用存儲大綱所生成的執行計劃也不會放入plan baseline里。
SQL語句的plan history以及plan baseline所涉及到的表是存放在SQL Management Base (SMB)里的,SMB里同樣也存放了SQL Profiles。而SMB是數據字典的一部分,存放在SYSAUX表空間里。SMB所占用的空間會自動定期的刪除。
2. 執行計劃管理的測試
·
Oracle 11g提供了一個視圖:dba_sql_plan_baselines,可以在該視圖里查詢某條SQL語句相關的plan history以及plan baseline。我們來看下面的例子。
首先創建一個測試表。
SQL> create table t1(skew number, padding varchar2(100));
SQL> insert into t1 select rownum,object_name from dba_objects;
SQL> commit;
SQL> set autotrace traceonly exp stat;
SQL> select * from t1 where skew=200;
SQL> select * from t1 where skew=200;
盡管執行兩次,但是這時去查詢dba_sql_plan_baselines,試圖找到SQL文本為select * from t1 where skew=200的記錄時,會發現沒有記錄,因為optimizer_capture_sql_plan_baselines缺省為false。我們將該參數設置為true以后繼續測試。
SQL> alter session set optimizer_capture_sql_plan_baselines=true;
SQL> select * from t1 where skew=200; --全表掃描
SQL> select * from t1 where skew=200; --全表掃描
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted, autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
我們可以看到,文本為“select * from t1 where skew=200”的SQL語句在plan history里產生了一個執行計劃。其中,
sql_handle表示SQL語句的句柄;
plan_name則表示該SQL的執行計劃的名字;
origin表示該執行計劃是如何進入plan history的,該列值為AUTO-CAPTURE則說明是由優化器自動加入的,如果為MANUAL則說明是由DBA手工加入的;
enabled表示是否被啟用了,YES表示啟用,NO表示禁用。如果某個執行計劃為禁用,則優化器根本就不會考慮使用該執行計劃;
accepted表示是否接受,也就是是否進入了plan baseline。我們看到這里的accepted為YES,說明該SQL的執行計劃進入了plan baseline里;
autopurge表示該執行計劃是否為定期自動刪除,YES表示是,NO表示否。
我們繼續測試,在skew上添加一個索引,從而讓原來的SQL不走全表掃描,而改走索引掃描。
SQL> create index idx_t1 on t1(skew);
SQL> exec dbms_stats.gather_table_stats(user,'t1',cascade=>true);
SQL> select * from t1 where skew=200; --索引掃描
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089cdbd90e8e AUTO-CAPTURE YES NO YES
這時我們可以看到,dba_sql_plan_baselines視圖里多了一個執行計劃,也就是我們后面那個使用了索引掃描的執行計劃。而該執行計劃的accepted為NO,說明該計劃并沒有進入plan baseline里,但是進入了plan history里。
這時,我們可以通過調用dbms_spm包來手工將走索引的執行計劃加入到plan baseline里。如下所示,將accepted改為YES。
SQL> dbms_spm.alter_sql_plan_baseline(
sql_handle => 'SYS_SQL_abc0a2c042fa089c',
plan_name => 'SYS_SQL_PLAN_42fa089c844cb98a',
attribute_name => 'ACCEPTED',
attribute_value => 'YES');
然后再次查詢dba_sql_plan_baselines視圖,可以發現后面的執行計劃的accepted變為了YES。
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089cdbd90e8e AUTO-CAPTURE YES YES YES
如果我們要手工刪除plan baseline里的執行計劃,則可以調用dbms_spm里的存儲過程來實現。
SQL> var cnt number;
SQL> exec :cnt :=
dbms_spm.purge_sql_plan_baseline('SYS_SQL_abc0a2c042fa089c');
刪除指定SQL語句的執行計劃以后,再去查詢dba_sql_plan_baselines就會發現上面測試SQL語句的執行計劃不存在了。
從上面的描述可以看出,SQL Plan Management特性的主要作用就是通過引入一個SQL plan baseline,從而保證SQL語句執行計劃的穩定,進而保證系統性能的穩定。本質上,它屬于11g之前的存儲大綱的升級版,而且是自動實現的,不需要人工干預。