hint(提示)無疑是最基本的控制執行計劃的方式了;通過在SQL語句中直接嵌入優化器指令,進而使優化器在語句執行時強制的選擇hint指定的執行路徑,這種使用方式最大的好處便是方便和快捷,定制度也很高,通常在對某些SQL語句執行計劃進行微調的時候我會首選這種方式,不過盡管如此,hint在使用中仍然有很多不可忽視的問題;
使用hint過程中有一些值得注意的細則,首先便是要准確的識別對應的查詢塊,如果需要使用注釋也可以hint中聲明;對於使用別名的對象一律使用別名來引用,並且諸如“用戶名.對象”的引用方式也不被允許,這幾個都是我平時經常犯的錯誤,其實細心一點也就沒什麼關系了,不過最郁悶的是使用hint的過程中沒有任何提示信息可以參考!!譬如語句中使用了無效的hint,Oracle並不會給予你任何相關的錯誤信息,相反這些hint會在執行時被默默的忽略,像什麼都沒發生一樣。。
到這裡,我並不想討論如何正確的使用hint,我想說的是在Oracle中,仍然有很多可以控制執行計劃的機制,11g中,有三種基於優化器hint的執行計劃控制方式:
1.OUTLINE(大綱)
2.SQL PROFILE(概要文件)
3.SQL BASELINE(基線)
這些方式的使用比較hint更加的系統,完備,它們的出現很大程度上提高了hint這種古老的控制方式的實用性。
OUTLINE(大綱)
OUTLINE的原理是解析SQL語句的執行計劃,在此過程中確定一套可以有效的強制優化器選擇某個執行計劃的hints,然後保存這些hints,當下次發生”相同“查詢的時候,優化器便會忽略當前的統計信息因素,選用OUTLINE中記錄的hints來執行查詢,達到控制執行計劃的目的。
OUTLINE的創建通常有兩種方式,一種使用create outline語句,另一種便是借助於專屬的DBMS_OUTLN包,使用Create outline方式時我們需要注明完整查詢語句:
復制代碼 代碼如下:
SQL> create outline my_test_outln for category test on
2 select count(*) from scott.emp;
Outline created.
相比之下,DBMS_OUTLN.CREATE_OUTLINE方式允許通過已經保存在緩存區中的SQL語句的hash值來創建outline,因此更加常用,下面是簽名:
復制代碼 代碼如下:
DBMS_OUTLN.CREATE_OUTLINE (
hash_value IN NUMBER,
child_number IN NUMBER,
category IN VARCHAR2 DEFAULT 'DEFAULT');
category用於指定OUTLINE的分類,在一個會話中只能使用一種分類,分類的選擇由參數USE_STORED_OUTLINES決定,該參數的默認值為FALSE,表示不適用OUTLINE,設置成TRUE則選用DEFAULT分類下的OUTLINE,如果需要使用非DEFAULT分類下的OUTLINE,可以設置該參數值為對應的分類的名稱。
關於OUTLINE的視圖通常可以查詢DBA_OUTLINES,DBA_OUTLINE_HINTS,數據庫中OUTLN用戶下也有三張表用於保存OUTLINE信息,其中OL#記載了每一個OUTLINE的完整定義。
復制代碼 代碼如下:
SQL> select TABLE_NAME,OWNER from all_tables where owner='OUTLN';
TABLE_NAME OWNER
------------------------------ ------------------------------
OL$ OUTLN
OL$HINTS OUTLN
OL$NODES OUTLN
-- 查詢當前系統中已有的OUTLINE已經對應OUTLINE使用的hints:
[sql]
SQL> select category,ol_name,hintcount,sql_text from outln.ol$;
CATEGORY OL_NAME HINTCOUNT SQL_TEXT
---------- ------------------------------ ---------- --------------------------------------------------
TEST MY_TEST_OUTLN 6 select count(*) from scott.emp
DEFAULT SYS_OUTLINE_13080517081959001 6 select * from scott.emp where empno=7654
-- 查詢對應OUTLINE上應用的hints
SQL> select name, hint from dba_outline_hints where name = 'SYS_OUTLINE_13080517081959001';
NAME HINT
------------------------------ --------------------------------------------------------------------------------
SYS_OUTLINE_13080517081959001 INDEX_RS_ASC(@"SEL$1" "EMP"@"SEL$1" ("EMP"."EMPNO"))
SYS_OUTLINE_13080517081959001 OUTLINE_LEAF(@"SEL$1")
SYS_OUTLINE_13080517081959001 ALL_ROWS
SYS_OUTLINE_13080517081959001 DB_VERSION('11.2.0.1')
SYS_OUTLINE_13080517081959001 OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
SYS_OUTLINE_13080517081959001 IGNORE_OPTIM_EMBEDDED_HINTS
6 rows selected.
使用OUTLINE來鎖定執行計劃的完整實例:
復制代碼 代碼如下:
-- 執行查詢
SQL> select * from scott.emp where empno=7654;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
-- 查看該查詢的執行計劃
-- 注意這裡的hash_value和child_number不可作為DBMS_OUTLN.CREATE_OUTLINE參數值,這些只是PLAN_TABLE中保存的執行計劃的值!!!
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID 40t73tu9dst5y, child number 1
-------------------------------------
select * from scott.emp where empno=7654
Plan hash value: 2949544139
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPNO"=7654)
19 rows selected.
-- 通過v$sql視圖獲取查詢sql語句的hash_value和child_number
SQL> select sql_id,hash_value,child_number,sql_text from v$sql
2 where sql_text like 'select * from scott.emp where empno%';
SQL_ID HASH_VALUE CHILD_NUMBER SQL_TEXT
------------- ---------- ------------ --------------------------------------------------
40t73tu9dst5y 2463917246 0 select * from scott.emp where empno=7654
-- 創建OUTLINE,指定為默認DEFAULT分類
SQL> exec dbms_outln.create_outline(2463917246,0,'DEFAULT');
PL/SQL procedure successfully completed.
-- SESSION級別設置USE_STORED_OUTLINES參數為TRUE,啟用OUTLINE
SQL> ALTER SESSION SET USE_STORED_OUTLINES=TRUE;
Session altered.
-- 重新執行查詢,可以看到計劃與原先的一致,同時在執行計劃的Note中顯示了使用了outline "SYS_OUTLINE_13080517081959001"
SQL> set autotrace traceonly
SQL> select * from scott.emp where empno=7654;
Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPNO"=7654)
Note
-----
- outline "SYS_OUTLINE_13080517081959001" used for this statement
Statistics
----------------------------------------------------------
1495 recursive calls
147 db block gets
262 consistent gets
5 physical reads
632 redo size
896 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
24 sorts (memory)
0 sorts (disk)
1 rows processed
使用非DEFAULT分類下的OUTLINE
復制代碼 代碼如下:
-- 查看當前可用的OUTLINE
SQL> select category,ol_name,hintcount,sql_text from outln.ol$;
CATEGORY OL_NAME HINTCOUNT SQL_TEXT
---------- ------------------------------ ---------- --------------------------------------------------
TEST MY_TEST_OUTLN 6 select count(*) from scott.emp
DEFAULT SYS_OUTLINE_13080517081959001 6 select * from scott.emp where empno=7654
-- 設置使用test分類下的OUTLINE
SQL> ALTER SESSION SET USE_STORED_OUTLINES=test;
Session altered.
-- 執行計劃Note顯示使用了OUTLINE "MY_TEST_OUTLN"
SQL> set autotrace traceonly
SQL> select count(*) from scott.emp;
Execution Plan
----------------------------------------------------------
Plan hash value: 2937609675
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------
Note
-----
- outline "MY_TEST_OUTLN" used for this statement
Statistics
----------------------------------------------------------
34 recursive calls
148 db block gets
22 consistent gets
0 physical reads
540 redo size
526 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
1 rows processed
關於OUTLINE的一些注意事項:
1,OUTLINE匹配SQL語句有點類似cusor_sharing參數中的similar,也就是說即使不同hash值的SQL語句也可能使用相同的OUTLINE,如:
復制代碼 代碼如下:
SQL> ALTER SESSION SET USE_STORED_OUTLINES=test;
Session altered.
-- 使用不相同的SQL語句 同樣使用了和之前相同的OUTLINE
SQL> set autotrace traceonly
SQL> SELECT COUNT(*)FROM scott.emp;
Execution Plan
----------------------------------------------------------
Plan hash value: 2937609675
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------
Note
-----
- outline "MY_TEST_OUTLN" used for this statement
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1 consistent gets
0 physical reads
0 redo size
526 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
-- 查詢v$sql可以看到兩條語句是不同的
SQL> select sql_id,hash_value,child_number,sql_text from v$sql
2 where sql_text like '%scott.emp%';
SQL_ID HASH_VALUE CHILD_NUMBER SQL_TEXT
------------- ---------- ------------ --------------------------------------------------
6xydpctfbwbm6 1555967590 0 select sql_id,hash_value,child_number,sql_text fro
m v$sql where sql_text like '%scott.emp%'
40t73tu9dst5y 2463917246 0 select * from scott.emp where empno=7654
abj9tmfcs15bm 2575340915 0 select count(*) from scott.emp
d16cs4nzg9vmk 1056239218 0 SELECT COUNT(*)FROM scott.emp
2,DBMS_OUTLN.CREATE_OUTLINE中hash_value的是SQL語句的hash值,不是sql_id,也不是執行計劃的hash_value。
3,DBMS_OUTLN.CREATE_OUTLINE無法像Create outline語句那樣自定義outline的名稱,這種方式創建的outline名稱是系統自動生成的,需要可以手動使用alter outline語句來修改。
4,可以通過v$sql中的OUTLINE_SID和OUTLINE_CATEGORY字段查詢到已經記錄到大綱中的sql語句。
復制代碼 代碼如下:
select sql_id,hash_value,child_number,OUTLINE_SID,OUTLINE_CATEGORY,sql_text from v$sql
where sql_text like '%scott.emp%'
SQL_ID HASH_VALUE CHILD_NUMBER OUTLINE_SID OUTLINE_CA SQL_TEXT
------------- ---------- ------------ ----------- ---------- ---------------------------------------------
6xydpctfbwbm6 1555967590 0 select sql_id,hash_value,child_number,sql_tex
t from v$sql where sql_text like '%scott.emp%'
40t73tu9dst5y 2463917246 0 DEFAULT select * from scott.emp where empno=7654
abj9tmfcs15bm 2575340915 0 TEST select count(*) from scott.emp
d16cs4nzg9vmk 1056239218 0 TEST SELECT COUNT(*)FROM scott.emp
SQL Profile(SQL概要文件)
SQL Profile基本上相當於OUTLINE的升級版本,也是功能上最強大的,很多時候都是在使用SQL優化顧問(SQL Tuning Advisor,STA)才會接觸到,同OUTLINE相同,SQL Profile同樣由一系列hint組成,使用SQL Profile我們可以在SQL語句執行的同時後台應用這些hint從而達到維持執行計劃穩定性的目的,事實上,相對OUTLINE它還具備一些特有的優勢,比如允許概要文件通過忽略常量應用到多條SQL語句上,同時還可以將任意hint集合與指定的SQL語句結合起來!!
在使用SQL Profile的過程中,參數SQLTUNE_CATEGORY實現了和OUTLINE中的USER_STORED_OUTLINE參數一樣的功能,於此同時,概要文件也會默認創建到DEFAULT分類中,通過為SQLTUNE_CATEGORY參數指定不同的分類名稱來啟用對應分類的SQL Profile;通常我們都是使用STA來創建概要文件,其實這些操作都直接間接的使用了DBMS_SQLTUNE.IMPORT_SQL_PROFILE過程,調用簽名如下:
復制代碼 代碼如下:
PROCEDURE IMPORT_SQL_PROFILE
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
SQL_TEXT CLOB IN
PROFILE SQLPROF_ATTR IN
NAME VARCHAR2 IN DEFAULT
DESCRIPTION VARCHAR2 IN DEFAULT
CATEGORY VARCHAR2 IN DEFAULT
VALIDATE BOOLEAN IN DEFAULT
REPLACE BOOLEAN IN DEFAULT
FORCE_MATCH BOOLEAN IN DEFAULT
PROCEDURE IMPORT_SQL_PROFILE
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
SQL_TEXT CLOB IN
PROFILE_XML CLOB IN
NAME VARCHAR2 IN DEFAULT
DESCRIPTION VARCHAR2 IN DEFAULT
CATEGORY VARCHAR2 IN DEFAULT
VALIDATE BOOLEAN IN DEFAULT
REPLACE BOOLEAN IN DEFAULT
FORCE_MATCH BOOLEAN IN DEFAULT
可以看到SQL Profile的創建是通過對SQL_TEXT指定hint集來完成的,並非OUTLINE中的HASH_VALUE,同時PROFILE字段的類型顯示使用的SQLPROF_ATTR,PROFILE_XML字段也是需要通過獲取V$SQL_PLAN視圖的OTHER_XML字段來填充hint集的,可惜的是在官檔中並沒有提及這一概要文件的重要過程,因此無法詳細了解它的使用細節,實際使用中還是建議使用STA來完成SQL Profile的創建。Kerry Osborne曾利用該過程來實現通過SQL_ID來創建SQL Profile,同時給出了利用IMPORT_SQL_PROFILE過程自定義hint集合來強制改變執行計劃的解決方案【可以訪問kerryosborne.oracle-guy.com獲取詳細信息】
基線(BASELINE)
BASELINE更像是一個性能的指標,oracle會通過基線來維護和消除系統的性能退化,基線的核心是一套具有特定名稱並與特定語句相聯系的hint,它可以像概要文件一樣匹配SQL語句,雖然對計劃的控制能力沒有概要文件那麼靈活,但它仍然是限制計劃不穩定性的重要方法,下面是基線的一些特點:
1,基線中不存在分類category。
2,每個SQL語句可以有多個基線,比如固定基線集合。
3,基線保存了hint和執行計劃的hash_value,因此優化器在判定是否采用基線時還需要驗證是否有對應的計劃存在。
4,可以通過設置OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES為true來為每一條執行過的SQL語句自動創建基線,默認情況下不會創建基線。
5,通過查詢視圖DBA_SQL_PLAN_BASELINES可以獲得已經創建的基線。
6,使用dbms_spm.load_plans_from_cursor_cache過程可以為一條緩存的SQL語句創建基線。
7,在11g中,默認會使用已經存在的基線維持執行計劃的穩定性。
為指定SQL語句創建基線
復制代碼 代碼如下:
-- 仍然使用OUTLINE中的示例查詢
SQL> select * from scott.emp where empno=7654;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------
SQL_ID 40t73tu9dst5y, child number 0
-------------------------------------
select * from scott.emp where empno=7654
Plan hash value: 2949544139
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPNO"=7654)
19 rows selected.
-- 創建BASELINE,注意參數為SQL_ID和PLAN_HASH_VALUE
SQL> var ret number
SQL> exec :ret := dbms_spm.load_plans_from_cursor_cache(-
> sql_id=>'&sql_id', -
> plan_hash_value=>&plan_hash_value,-
> fixed=>'&fixed');
Enter value for sql_id: 40t73tu9dst5y
Enter value for plan_hash_value: 2949544139
Enter value for fixed: NO
PL/SQL procedure successfully completed.
-- 再次運行查詢可以發現在執行計劃輸出的Note中顯示使用了基線SQL_PLAN_bmwra43zx42kr695cc014
SQL> set autotrace traceonly
SQL> select * from scott.emp where empno=7654;
Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPNO"=7654)
Note
-----
- SQL plan baseline "SQL_PLAN_bmwra43zx42kr695cc014" used for this statement
Statistics
----------------------------------------------------------
747 recursive calls
14 db block gets
117 consistent gets
0 physical reads
2956 redo size
1028 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed