一、oracle 11G 執行計劃管理原理
oracle11G開始,引入了sql執行計劃管理(SQL Plan Management)這個新特性
通過啓用該特性,某條語句如果產生了一個新的執行計劃,只有在它的性能比原來的執行計劃好的情況下,纔會被使用。
優化器維護兩個列表:
plan history
plan baseline
11G也支持手工維護plan history,作爲對自動維護plan history的功能補充
plan baseline是plan history的一個子集
1.初始化參數 OPTIMIZER_CAPTURE_PLAN_BASELINES 設置爲true,則會自動捕獲SQL的執行計劃。
SQL第一次執行,plan history和plan baseline列表都爲空,這時產生的執行計劃會同時進入到plan history,plan baseline列表中
以後同一語句所產生的新執行計劃都會先進入到plan history中,然後同plan baseline中的執行計劃進行比較,只有成本比plan baseline
中的要低纔會進入到plan baseline中
2.使用dbms_spm包手工處理,這可以讓你手工管理SQL Plan baseline。使用該包,你可以直接將SQL的執行計劃從 shared pool里加載到
plan baseline裏,也可以將已經存在的SQL Tuning Set加載到plan baseline裏。同時,dbms_spm還可以將plan history裏的執行計劃加入到
plan baseline裏。反之也可以用該包將plan baseline裏的執行計劃移出去。
3.plan baseline 裏的執行計劃是如何被使用的呢?
oracle 提供了一個初始化參數:OPTIMIZER_USE_PLAN_BASELINES,該參數缺省爲true,表示要求優化器考慮使用plan baseline裏的執行計劃,
每次優化器解析SQL語句的時候,首先仍然使用11G之前的傳統方式產生一個成本最低的執行計劃,然後看初始化參數:
OPTIMIZER_USE_PLAN_BASELINES是否設置爲true,如果爲false,則直接返回所生成的執行計劃。否則如果爲true,則去plan_history裏找是否存在
相同的執行計劃,如果找到,則再去plan baseline裏找是否存在相同的執行計劃,如果也找到了,則直接返回該執行計劃。如果在plan_history裏沒找到
相同的執行計劃,則將生成的執行計劃加入到plan history裏,然後將執行計劃與plan baseline裏已經存在的進行比較,看哪個成本低就選取哪個執行計劃
SQL語句的plan history以及plan baseline所涉及到的表是存放在SQL Management Base (SMB)裏的,SMB裏同樣也存放了SQL Profiles。而SMB是數據字典的一部分,存放在SYSAUX表空間裏。SMB所佔用的空間會自動定期的刪除。
二、執行計劃管理的測試
oracle11g提供了一個視圖:dba_sql_plan_baselines,可以在該視圖裏查詢某條 SQL語句相關的plan history以及plan baseline。我們來看下面的例子
首先創建一個測試表.
SQL> create table t1(skew number,padding varchar2(100));
Table created.
SQL>
SQL> insert into t1 select rownum,object_name from dba_objects;
86632 rows created.
SQL> commit;
SQL> set autot trace exp stat
SQL> select * from t1 where skew=200;
Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 325 | 116 (1)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| T1 | 5 | 325 | 116 (1)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SKEW"=200)
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
10 recursive calls
0 db block gets
497 consistent gets
0 physical reads
0 redo size
607 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 325 | 116 (1)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| T1 | 5 | 325 | 116 (1)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SKEW"=200)
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
423 consistent gets
0 physical reads
0 redo size
607 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from dba_sql_plan_baselines;
no rows selected
儘管執行兩次,但是這時去查詢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;
Session altered.
SQL> select * from t1 where skew=200;
SKEW
----------
PADDING
--------------------------------------------------------------------------------
200
I_SQL$TEXT_PKEY
SQL> /
SKEW
----------
PADDING
--------------------------------------------------------------------------------
200
I_SQL$TEXT_PKEY
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 ENABLED ACCEPTED AUTOPURGE
------------------------------------------ --------- --------- ---------
1.2376E+19
SQL_abc0a2c042fa089c
SQL_PLAN_arh52s11gn24wdbd90e8e
AUTO-CAPTURE YES YES YES
sql_handle表示SQL語句的句柄;
plan_name則表示SQL執行計劃的名字;
origin表示該執行計劃是如何進入到plan_history的,該列值爲AUTO-CAPTURE,則說明是由優化器自動加入的,
如果是MANUAL則說明是由DBA手工加入的;
enabled表示是否被啓用了;如果某個執行計劃爲禁用,則優化器根本就不會考慮使用該計劃;
accepted表示是否接受,也就是是否進入了plan baseline。
autopurge表示是否爲定期自動刪除。
我們繼續測試,在skew列上添加一個索引,從而讓原來的SQL不走全表掃描,而改走索引掃描。
SQL> create index idx_skew on t1(skew);
Index created.
SQL> exec dbms_stats.gather_table_stats('SYS','T1',cascade=>true);
PL/SQL procedure successfully completed.
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 ENABLED ACCEPTED FIXED AUTOPURGE
---------- ------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------ ------------------------------------------ --------- --------- --------- ---------
1.2376E+19 SQL_abc0a2c042fa089c SQL_PLAN_arh52s11gn24w22142e90 AUTO-CAPTURE YES NO NO YES
1.2376E+19 SQL_abc0a2c042fa089c SQL_PLAN_arh52s11gn24wdbd90e8e AUTO-CAPTURE YES YES NO YES
這時我們可以看到,dba_sql_plan_baselines視圖裏多了一個執行計劃,也就是我們後面那個使用了索引掃描的執行計劃。而該執行計劃的accepted爲NO,說明該計劃並沒有
進入到plan baseline裏,但進入了plan history裏。
這時我們可以通過調用dbms_spm包來手工將走索引的執行計劃加入到plan baseline裏。如下所示,將accepted改爲YES。
執行DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE前:
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge from dba_sql_plan_baselines where plan_name='SQL_PLAN_arh52s11gn24w22142e90';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENABLED <span style="color:#ff0000;">ACCEPTED </span>FIXED AUTOPURGE
---------- ------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------ ------------------------------------------ --------- --------- --------- ---------
1.2376E+19 SQL_abc0a2c042fa089c SQL_PLAN_arh52s11gn24w22142e90 AUTO-CAPTURE YES <span style="color:#ff0000;">NO </span>NO YES
SQL> declare
l_plans_altered clob;
begin
l_plans_altered := DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(sql_handle => 'SQL_abc0a2c042fa089c',
plan_name => 'SQL_PLAN_arh52s11gn24w22142e90',
time_limit => DBMS_SPM.AUTO_LIMIT,
verify => 'yes',
commit => 'yes');
end; 2 3 4 5 6 7 8 9
10 /
PL/SQL procedure successfully completed.
SQL>
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge from dba_sql_plan_baselines where plan_name='SQL_PLAN_arh52s11gn24w22142e90';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENABLED <span style="color:#ff0000;">ACCEPTED </span>FIXED AUTOPURGE
---------- ------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------ ------------------------------------------ --------- --------- --------- ---------
1.2376E+19 SQL_abc0a2c042fa089c SQL_PLAN_arh52s11gn24w22142e90 AUTO-CAPTURE YES <span style="color:#ff0000;">YES</span> NO YES
SQL>
不過後來發現,要把accepted變爲NO卻是不可以了,只有先刪除,然後再重新生成讓優化器自動加入到plan history中
SQL> DECLARE
l_plans_dropped PLS_INTEGER;
BEGIN
l_plans_dropped := DBMS_SPM.drop_sql_plan_baseline(sql_handle => 'SQL_abc0a2c042fa089c',
plan_name => 'SQL_PLAN_arh52s11gn24w22142e90');
end; 2 3 4 5 6
7 /
PL/SQL procedure successfully completed.