一、基礎概念
Oracle 11g開始,提供了一種新的固定運行計划的方法,即SQL plan baseline,中文名SQL運行計划基線(簡稱基線),能夠覺得是OUTLINE(大綱)或者SQL PROFILE的改進版本號。基本上它的主要作用能夠歸納為例如以下兩個:
1、穩定給定SQL語句的運行計划。防止運行環境或對象統計信息等等因子的改變對SQL語句的運行計划產生影響!
2、降低數據庫中出現SQL語句性能退化的概率。理論上不同意一條語句切換到一個比已經運行過的運行計划慢非常多的新的運行計划上!
注意:
1、從Oracle的發展角度來看,預計這樣的方法是Oracle發展和改進的方向。現在outline已經被廢棄,sql profile預計在興許的發行版本號中也難有改進,因此,對於從11g開始接觸Oracle的朋友來說,一定要對sql計划基線有所了解,由於這是以后的主流!
2、SQL運行計划基線保存在數據字典中。查詢優化器會自己主動推斷使用他們。
二、工作機制
從Oracle 11g開始,因為基線的存在,一條語句的解析過程大概例如以下:
- SQL語句被硬解析后。CBO(優化器)會產生非常多個的運行計划。CBO從中選擇一個成本最低運行計划。
- 基於SQL語句的文本形成一個哈希值(signature),通過這個哈希值來檢查數據字典中是否存在相同的基線。
- 假設基線存在,優化器會對剛剛產生的運行計划和保存在SQL plan baseline中的運行計划進行比較。
- 假設基線中有與CBO剛產生的運行計划的匹配的SQL運行計划存在。而且被標記為可接受(‘accepted’)。則這個CBO生成的運行計划被啟用。
- 假設基線中沒有匹配的SQ運行計划存在,CBO評估基線中被標記為‘accepted’的的多個運行計划。並選擇當中cost最低的運行計划。(注意,一個語句的基線能夠有多個運行計划被保存,這是與其它Outline和SQL profiel都不同的地方)
- 假設剛剛硬解析過程中CBO選擇的運行計划比保存在基線中的運行計划COST都低,這個新生成的運行計划被標記為‘not-accepted’並保存在基線中。
直到這個運行計划被演化且驗證后才會被考慮使用,即標記為accepted(演化和驗證。能夠簡單理解為Oracle確認這個運行計划能夠帶來更好的性能)。
Oracle 就是通過上面這樣的方式來確保SQL語句的性能不會退化(即第一部分中我歸納的第二個主要作用),稱為“
運行計划保守選擇策略”
三、基線的一些特點
簡單歸納例如以下幾個
- 通過OPTIMIZER_USE_SQL_PLAN_BASELINE來控制Oracle是否使用基線,默認值為TRUE。即會自己主動使用基線。
- 11g中默認是不會自己主動創建基線
- 與OUTLINE和SQL Profile不同,基線中不存在分類的概念
- 與OUTLINE和SQL Profile不同。每一個SQL語句能夠有多個基線。
Oracle依據制定的規則來推斷詳細是否哪個基線
- 基線針對RAC中全部的實例都生效
- 基線有兩個表示,一個為sql_handle。能夠理解為表示語句文本的唯一標識,一個為sql_plan_name能夠理解為運行計划的唯一標識
- 不能像sql profile一樣通過force_matching屬性將字面值不一樣的SQL語句使用一個基線應用多個語句。
三、創建基線的幾種方式
1、自己主動捕獲基線。通過將optimizer_cature_sql_plan_baselines設置為true。優化器為反復運行兩次以上的SQL語句生成並保存基線(能夠系統級或會話級改動)
2、從SQL調優集合中載入。通過使用包dbms_spm.load_plans_from_sqlset來從SQL調優集合中載入基線
DECLARE
l_plans_loaded PLS_INTEGER;
BEGIN
l_plans_loaded := DBMS_SPM.load_plans_from_sqlset(
sqlset_name => 'my_sqlset');
END;
/
3、從庫緩存中載入,通過包dbms_spm.load_plans_from_cursor_cache函數為一條已經在游標緩存中的語句創建基線
DECLARE
l_plans_loaded PLS_INTEGER;
BEGIN
l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(sql_id => '1fkh93md0802n',plan_hash_value=>null);
END;
/
備注:能夠有多種方式載入,比如能夠依據sql文本進行模糊匹配、SQL語句解析的username等等方式,詳細見文檔
四、基線的幾種狀態
一個SQL語句相應的基線,我將它們歸納為三種狀態
- accepted(可接受),僅僅有這樣的狀態的基線,優化器才會考慮此基線中的運行計划
- no-accepted(不可接受)。這樣的狀態的基線,優化器在SQL語句解析期間不會考慮。這樣的狀態的基線必須通過演化和驗證通過后,轉變為accepted狀態后。才會被優化器考慮使用
- fixed為yes(固定),這樣的狀態的基線固有最高優先級!
比其它兩類基線都要優先考慮
五、查看基線
1、基本視圖:dba_sql_plan_baselines、
dba_sql_management_config
2、底層視圖:
sqlobj$data 、 sqlobj$ (保存詳細的hint),例如以下查看基線中保存的運行計划語句:
select
extractvalue(value(d), '/hint') as outline_hints
from
xmltable('/outline_data/hint'
passing (
select
xmltype(comp_data) as xmlval
from
sqlobj$data sod, sqlobj$ so
where so.signature = sod.signature
and so.plan_id = sod.plan_id
and comp_data is not null
and name like '&baseline_plan_name'
)
) d;
3、通過函數來查看基線的具體信息:
select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja1790cce5f0e'));
六、演化基線
為了驗證基線中一個處於不可接受狀態的運行計划是否比一個處於可接受狀態的運行計划具有更高的效率。必須通過演化來驗證,須要讓優化器以不同的運行計划來運行這條SQL語句。觀察不可接受狀態的運行計划基線是否會帶來更好的性能,假設性能確實更高,這個不可接受狀態的基線將會轉換為可接受狀態。演化的方式有兩種:
1、手工執行執行
SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_xxxxxxxxxxxxx') From dual;
還有time_limit/verify/commit幾個參數,能夠參考文檔
2、調優包實現基線的自己主動演化,能夠理解為,啟動一個調度任務,周期性的檢查是否有不可接受狀態的基線能夠被演化
七、改動基線
能夠通過dbms_spm.alter_sql_plan_baseline包來改動基線的一些屬性。主要有例如以下幾個屬性
- ENABLED :設置該屬性的值為NO告訴Oracle 11g暫時禁用某個計划,一個SQL計划必須同一時候標記為ENABLED和ACCEPTED,否則CBO將忽略它
- FIXED:設置為YES,那個計划將是優化器唯一的選擇[最高優先級]。即使假設某個計划可能擁有更低的成本。這讓DBA能夠撤銷SMB的默認行為,對於轉換一個存儲概要進入一穩定的SQL計划基線特別實用,注意當一個新計划被加入到被標記為FIXED的SQL計划基線,該新計划不能被利用除非它申明為FIXED狀態
- AUTOPURG:設置這個屬性的值為NO告訴Oracle 11g無限期保留它。從而不用操心SMB的自己主動清除機制
- plan_name : 改變SQL plan 名字
- description : 改變SQL plan描寫敘述
語法:
SET SERVEROUTPUT ON
DECLARE
v_text PLS_INTEGER;
BEGIN
v_text := DBMS_SPM.alter_sql_plan_baseline(sql_handle => 'SYS_SQL_xxxxxx',plan_name => 'SYS_SQL_PLAN_xxxxxxxxx',
attribute_name => 'fixed',attribute_value => 'YES');
DBMS_OUTPUT.put_line('Plans Altered: ' || v_text );
END;
/
八、遷移基線
dbms_spm提供了多個過程來在數據庫之間遷移SQL計划基線
- create_stgtab_baseline創建一個計划基線保存表
- pack_stgtab_baseline將基線從數據字典拷貝到第一步的表中
- unpack_stgtab_baseline將基線從保存表中拷貝到遷移數據庫的數據字典中
大概步驟例如以下:
1、創建一張保存數據字典中基線表內容的用戶表
exec dbms_spm.create_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT',tablespace_name =>'');
2、將數據字典中基線表的內容 插入到 第一步創建的用戶表中
exec :i := dbms_spm.pack_stgtab_baseline(table_name => 'BASELINE_TEST', table_owner => 'SCOTT');
備注:能夠支持多種方式插入,比如包括特定字符的SQL相關的基線。sql_handle來精確識別一個基線,詳細見文檔
3、通過遷移工具遷移用戶表
exp/imp or expdp/impdp
4、將遷移過來的用戶表中保存的基線內容 插入到當前庫的數據字典中。從而實現遷移
exec :i := dbms_spm.unpack_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT');
備注:能夠支持多種方式,與步驟2一樣。詳細見文檔
九、刪除基線
- 能夠通過dbms_SPM.drop_sql_plan_baseline包來手工刪除數據字典里的基線
- 為使用的基線。fixed為no的基線,將在一定的保留期后自己主動刪除(可查看dba_sql_management_config視圖)
手工刪除方法例如以下
SET SERVEROUTPUT ON
DECLARE
v_text PLS_INTEGER;
BEGIN
v_text := DBMS_SPM.drop_sql_plan_baseline(sql_handle => 'SYS_SQL_7b76323ad90440b9',plan_name => NULL);
DBMS_OUTPUT.put_line(v_text);
END;
/
十、將一個SQL語句固定為我們期望的運行計划
我一般通過例如以下幾步實現(僅供參考)
1、為這個SQL語句創建基線
2、給這個SQL語句加入hint賴宇星,確保SQL語句加入hint后的運行計划與我們期望一樣
3、將第2步產生的運行計划。加入到第一步創建的基線中(注意,前面已經說過,一個SQL語句能夠有多個基線。)
4、刪除基線中第1步創建的那個運行計划(這樣,我們就能夠確保基線中僅僅有我們期望的運行計划,即保存第2步SQL語句的運行計划)
5、驗證是否生效
興許有演示樣例。加深理解!
十一、演示樣例(將一個SQL語句固定為我們期望的運行計划)
首先運行兩個結構同樣的語句,以下的實驗通過SQL計划基線。將一個語句的運行計划通過還有一個語句的運行計划來固定
SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;
未選定行
SQL> alter system flush shared_pool;
系統已更改。
SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711;
ID NAME TYPE
---------- ------------------------------ ---------------
711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select /* outlinetest3 */ /*+ index(dh_stat) */ * from dh_stat where id=711;
ID NAME TYPE
---------- ------------------------------ ---------------
711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time
2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME
------------------------------------------------------- ------------- ---------- ------------ --------------- --------
select /* outlinetest2 */ /*+ full(dh_stat) */ * from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:27:31
h_stat where id=711
select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41
dh_stat where id=711
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 4vaj9fgjysy9c, child number 0
-------------------------------------
select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where
id=711
Plan hash value: 1845196118
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 124 (100)| |
|* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"=711)
已選擇19行。
SQL> select * from table(dbms_xplan.display_cursor('fm35jcmypb3qu','',''));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID fm35jcmypb3qu, child number 0
-------------------------------------
select /* outlinetest3 */ /*+ index(dh_stat) */ * from dh_stat where
id=711
Plan hash value: 2780970545
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=711)
已選擇20行。
SQL> DECLARE
2 k1 pls_integer;
3 begin
4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
5 sql_id=>'4vaj9fgjysy9c',
6 plan_hash_value=>1845196118
7 );
8 end;
9 /
PL/SQL 過程已成功完畢。
SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME SQL_TEXT ACC
------------------------------ ------------------------------ ------------------------------------------------------- ---
SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES
h_sta
剛生產sql plan baseline的時候。第一次查詢,無法找到運行計划,直到第二次運行的時候。才干看到,例如以下
SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711;
ID NAME TYPE
---------- ------------------------------ ---------------
711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));
PLAN_TABLE_OUTPUT
---------------------------------------------------------
SQL_ID: 4vaj9fgjysy9c cannot be found
SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time
2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME
------------------------------------------------------- ------------- ---------- ------------ --------------- --------
select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41
dh_stat where id=711
SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711;
ID NAME TYPE
---------- ------------------------------ ---------------
711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time
2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME
------------------------------------------------------- ------------- ---------- ------------ --------------- --------
select /* outlinetest2 */ /*+ full(dh_stat) */ * from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:30:54
h_stat where id=711
select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41
dh_stat where id=711
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 4vaj9fgjysy9c, child number 0
-------------------------------------
select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where
id=711
Plan hash value: 1845196118
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 124 (100)| |
|* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"=711)
Note
-----
- SQL plan baseline SQL_PLAN_13g6p1maja17934f41c8d used for this statement
已選擇23行。
將符合我們預期的運行計划的載入到第一次生成的sql baseline中!
SQL> DECLARE
2 k1 pls_integer;
3 begin
4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
5 sql_id=>'fm35jcmypb3qu',
6 plan_hash_value=>2780970545,sql_handle=>'SYS_SQL_11bcd50cd51504e9'
7 );
8 end;
9 /
PL/SQL 過程已成功完畢。
能夠看到,SYS_SQL_11bcd50cd51504e9下眼下有兩個plan_name
SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME SQL_TEXT ACC
------------------------------ ------------------------------ ------------------------------------------------------- ---
SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja1790cce5f0e select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES
h_sta
SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES
h_sta
刪除第一個plan_name。即將我們不須要的運行計划版本號去除掉。
SQL> DECLARE
2 k1 pls_integer;
3 begin
4 k1 := DBMS_SPM.drop_sql_plan_baseline ( sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja17934f41c8d');
5 end;
6 /
PL/SQL 過程已成功完畢。
通過以下的一部分測試,我們能夠看到。新的SQL計划基線已經正常生效。及時語句中包括full提示。運行計划也走索引定位數據
SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711;
ID NAME TYPE
---------- ------------------------------ ---------------
711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 4vaj9fgjysy9c, child number 1
-------------------------------------
select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where
id=711
Plan hash value: 2780970545
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=711)
Note
-----
- SQL plan baseline SQL_PLAN_13g6p1maja1790cce5f0e used for this statement
已選擇24行。
能夠通過dba_sql_plan_baselines來顯示可用的SQL計划基線的一般信息,也能夠通過例如以下這樣的方式顯示運行SQL計划基線的具體信息!
select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja1790cce5f0e'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------
SQL handle: SYS_SQL_11bcd50cd51504e9
SQL text: select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where
id=711
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_13g6p1maja1790cce5f0e Plan id: 214851342
Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------
Plan hash value: 2780970545
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=711)
已選擇26行。
查看SQL計划基線中保存的hint提示集合
SQL> conn /as sysdba
已連接。
SQL> select
2 extractvalue(value(d), '/hint') as outline_hints
3 from
4 xmltable('/outline_data/hint'
5 passing (
6 select
7 xmltype(comp_data) as xmlval
8 from
9 sqlobj$data sod, sqlobj$ so
10 where so.signature = sod.signature
11 and so.plan_id = sod.plan_id
12 and comp_data is not null
13 and name like '&baseline_plan_name'
14 )
15 ) d;
輸入 baseline_plan_name 的值: SQL_PLAN_13g6p1maja1790cce5f0e
原值 13: and name like '&baseline_plan_name'
新值 13: and name like 'SQL_PLAN_13g6p1maja1790cce5f0e'
OUTLINE_HINTS
-----------------------------------------------------------------------------------------------------------------------------------------------
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "DH_STAT"@"SEL$1" ("DH_STAT"."ID"))
選6行。
版權聲明:本文博客原創文章,博客,未經同意,不得轉載。