您好,登錄后才能下訂單哦!
我們都希望對于所有在Oracle數據庫中執行的SQL,CBO都能產生出正確的執行計劃,但實際情況卻并非如此,由于各種各樣的原因(比如目標SQL所涉及的對象的統計信息的不準確,或者CBO內部一些成本計算公式的先天缺陷等),導致有時CBO產生效率不高、甚至是錯誤的執行計劃。特別是CBO對目標SQL所產生的初始執行計劃是正確的,后來由于某種原因(比如統計信息的變更等)而導致CBO重新對其產生了錯誤的執行計劃,這種執行計劃的改變往往會導致目標SQL執行時間呈數量級的遞增,而且常常會讓我們很困惑:這個SQL原先跑得好好的,為什么突然就慢得讓人無法接受?其實這種SQL執行效率突然衰減往往是因為目標SQL執行計劃的改變。
我們當然希望這樣的改變永遠不要發生,即在Oracle數據庫中跑的所有SQL都能有正確的、穩定的執行計劃,但實際上在Oracle 11g的SPM(SQL Plan Management)出現之前,這一點是很難做到的。那么現在退而求其次,如果已經出現了執行墳墓的變更,即CBO已經產生了錯誤的執行計劃,我們應該怎么糾正呢?
我種情況下,我們通常會重新收集一下統計信息或者修改目標SQL(比如在目標SQL中加入Hint等)以糾正錯誤的執行計劃。但有時候重新收集統計信息并不能解決問題,更糟糕的是,很多情況下是沒有辦法修改目標SQL的SQL文本的(比如第三方開發的系統,修改不了源碼,或者目標SQL是前臺框架動態生成的等等),那么這種情況下我們該怎么辦呢?
在Oracle 10g/11g及其以后的版本中,我們可以使用SQL Profile或SPM(SQL Plan Management)來解決上述執行計劃變更的問題,用它們來調整、穩定目標SQL的執行計劃。
本文介紹使用SQL Profile來穩定執行計劃:
Oracle 10g中的SQL Profile(直譯為“SQL概要”)可以說是Oracle 9i中的Stored Outline(直譯為“存儲概要”)的進化。Stored Outline能夠實現的功能SQL Profile也完全能夠實現。
與Stored Outline相比,SQL Profile具備如下優點:
更容易生成、更改和控制
在SQL語句的支持上做得更好,也就是說適用范圍更廣。
使用SQL Profile可以很容易實現如下兩個目的:
鎖定或者說穩定執行計劃
在不能修改目標SQL的SQL文本的情況下使目標SQL語句按指定的執行計劃運行。
SQL Profile有兩種類型:一種是Automatic類型,另一種是Manual類型。下面分別介紹這兩種類型:
1. Automatic類型的SQL Profile
Automatic類型的SQL Profile其實就是針對目標SQL的一些額外的調整信息,這些信息存儲在數據字典里。當有了Automatic類型的SQL Profile后,Oracle在產生執行計劃時就會根據它對目標SQL所涉及的統計信息等內容做相應的調整,因而能夠在一定程度上避免產生錯誤的執行計劃。你不用擔心Automatic類型的SQL Profile的準確性,因為Oracle會使用類型于動態采用技術那樣的手段來保證這些額外調整信息相對準確。
Automatic類型的SQL Profile不會像Stored Outline那樣鎖定目標SQL的執行計劃,因為Automatic類型的SQL Profile的本質就是針對目標SQL的一些額外的調整信息,這些額外的調整信息需要與原目標SQL的相關統計信息等內容一起作用才能得到新的執行計劃,即原始SQL的統計信息等內容一旦發生變化,即使原有Automatic類型的SQL Profile并沒有改變,該SQL的執行也可能會發生變化。從這個意義上講,Automatic類型的SQL Profile并不能完全起到穩定目標SQL的執行計劃的作用,雖然它確實可以用來調整執行計劃。
看一個在不更改目標SQL的SQL文本的情況下使用Automatic類型的SQL Profile來調整執行計劃的實例:
創建測試表及相關操作:
zx@MYDB>create table t1 (n number); Table created. zx@MYDB>declare 2 begin 3 for i in 1..10000 loop 4 insert into t1 values(i); 5 end loop; 6 commit; 7 end; 8 / PL/SQL procedure successfully completed. zx@MYDB>select count(*) from t1; COUNT(*) ---------- 10000 zx@MYDB>create index idx_t1 on t1(n); Index created. zx@MYDB>exec dbms_stats.gather_table_stats(ownname=>USER,tabname=>'T1',method_opt=>'for all columns size 1',cascade=>true); PL/SQL procedure successfully completed. zx@MYDB>select /*+no_index(t1 idx_t1) */ * from t1 where n=1; N ---------- 1
從上述顯示內容可以看出,目標SQL走的是對表T1的全表掃描(Table Access Full),這個執行計劃顯然是錯誤,這里正確的執行墳墓應該是走索引IDX_T1的索引范圍掃描(Index Range Scan)。下面使用SQL Tuning Advisor對這條SQL生成Automatic類型的SQL Profile。
a.先創建一個名為my_sql_tuning_task_2的自動調整任務:
zx@MYDB>declare 2 my_task_name varchar2(30); 3 my_sqltext clob; 4 begin 5 my_sqltext:='select /*+no_index(t1 idx_t1) */ * from t1 where n=1'; 6 my_task_name:=dbms_sqltune.create_tuning_task( 7 sql_text=>my_sqltext, 8 user_name=>USER, 9 scope=>'COMPREHENSIVE', 10 time_limit=>60, 11 task_name=>'my_sql_tuning_task_1', 12 description=>'Task to tune a query on table t1'); 13 end; 14 / PL/SQL procedure successfully completed. zx@MYDB>select task_name,status,execution_start,execution_end from user_advisor_log; TASK_NAME STATUS EXECUTION_START EXECUTION_END ------------------------------ --------------------------------- ------------------- ------------------- my_sql_tuning_task_1 INITIAL
注:創建任務時可以使用SQL來創建,可以適用于SQL文本長的情況。詳情參考官方文檔。
b.執行上述自動調整任務
zx@MYDB>begin 2 dbms_sqltune.execute_tuning_task(task_name=>'my_sql_tuning_task_1'); 3 end; 4 / zx@MYDB>zx@MYDB>select task_name,status,execution_start,execution_end from user_advisor_log; TASK_NAME STATUS EXECUTION_START EXECUTION_END ------------------------------ --------------------------------- ------------------- ------------------- my_sql_tuning_task_1 COMPLETED 2017-02-28 10:59:43 2017-02-28 10:59:44 PL/SQL procedure successfully completed.
c.查看上述自動任務的調整結果
zx@MYDB>set long 9000 zx@MYDB>set longchunksize 1000 zx@MYDB>set linesize 800 zx@MYDB>select dbms_sqltune.report_tuning_task('my_sql_tuning_task_1') from dual;
從上述調整結果可以看到,Oracle現在告訴我們:它已經為目標SQL找到了更好的執行計劃,并且已經創建了針對該SQL的Automatic類型的SQL Profile。如果我們使用accecp_sql_profile接受了這個SQL Profile,則目標SQL的響應時間將會有86.24%的提升,邏輯讀將會有95%的提升,并且接受了該SQL Profile后目標SQL的執行計劃將會由原來的全表掃描變為索引范圍掃描。
上面Automatic類型的SQL Profile所產生的調整結果就是我們想要的,所以現在只需按Oracle的提示接受這個SQL Profile即可:
zx@MYDB>execute dbms_sqltune.accept_sql_profile(task_name =>'my_sql_tuning_task_1', task_owner => 'ZX', replace => TRUE,force_match=>true); PL/SQL procedure successfully completed.
接受此SQL Profile后我們來看一下效果,再次執行目標SQL:
zx@MYDB>select /*+no_index(t1 idx_t1) */ * from t1 where n=1; N ---------- 1
注意到Note部分有這樣的內容“SQL profile SYS_SQLPROF_015a82b353490000 used for this statement”這說明我們剛才接受的SQL Profile已經起了作用,該SQL Profile的名字為SYS_SQLPROF_015a82b353490000。從執行計劃中也可以看到,執行計劃確實已經改變了。
另外,DBMS_SQLTUNE.ACCEPT_SQL_PROFILE的輸入參數force_match的默認值為FALSE,表示只有在SQL文本完全匹配的情況下才會應用SQL Profile,這種情況下只要目標SQL的SQL文本發生一點變動,原有的SQL Profile將會失去作用,如果設置為TRUE,即使SQL有變動SQL Profile也會強制生效。
刪除SQL Profile
zx@MYDB>exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_015a82b353490000'); PL/SQL procedure successfully completed.
2. Manual類型的SQL Profile
Manual類型的SQL Profile本質上就是一堆Hint的組合,這一堆Hint的組合實際上來源于執行計劃中的Outline Data部分的Hint組合。Manual類型的SQL Profile同樣可以在不更改目標SQL的SQL文本的情況下,調整其執行計劃,而且更為重要的是,Manual類型的SQL Profile可以起到很好穩定目標SQL的執行計劃的作用,這一點是Automatic類型的SQL Profile所不具備的。
看一個使用Manual類型的SQL Profile實例固定執行計劃的實例,使用上面的t1表,刪除上面的SQL Profile,再次執行SQL
zx@MYDB>select /*+no_index(t1 idx_t1) */ * from t1 where n=1; N ---------- 1
從上述輸出可以看出執行計劃仍然走全表掃描。
現在來創建Manual類型的SQL Profile。這里使用了MOS上的一個腳本coe_xfr_sql_profile.sql。這個腳本用于從Shared Pool、AWR Repository中指定SQL的指定執行計劃的Outline Data部分的Hint組合,來創建Manual類型的SQL Profile。
使用coe_xfr_sql_profile.sql腳本的步驟為
針對目標SQL使用coe_xfr_sql_profile.sql產生能生成其Manual類型的SQL Profile的腳本A。
改寫目標SQL的文本,在其中使用合適的Hint,直到加入Hint后的SQL能走出我們想要的執行計劃。然后對加入合適Hint后的SQL使用腳本coe_xfr_sql_profile.sql,產生能生成其Manual類型的SQL Profile的腳本B。
用腳本B中的Outline Data部分的Hint組合替換掉腳本A的Outline Data部分的Hint組合。
執行腳本A生成針對原目標SQL的Manual類型的SQL Profile。
現在改寫上面的SQL,強制走索引:
zx@MYDB>select /*+index(t1 idx_t1) */ * from t1 where n=1; N ---------- 1
從執行計劃中可以看出SQL Id和對應的Plan hash value。
全表掃描的SQL Id:6chcc0pvvhqqm Plan hash value:3617692013
索引掃描的SQL Id:2ufquy7xs5nm5 Plan hash value:1369807930
a. 先使用coe_xfr_sql_profile.sql生成全表掃描SQL對應的腳本
zx@MYDB>@scripts/coe_xfr_sql_profile.sql Parameter 1: SQL_ID (required) Enter value for 1: 6chcc0pvvhqqm PLAN_HASH_VALUE AVG_ET_SECS --------------- ----------- 3617692013 .002 Parameter 2: PLAN_HASH_VALUE (required) Enter value for 2: 3617692013 Values passed to coe_xfr_sql_profile: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SQL_ID : "6chcc0pvvhqqm" PLAN_HASH_VALUE: "3617692013" SQL>BEGIN 2 IF :sql_text IS NULL THEN 3 RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).'); 4 END IF; 5 END; 6 / SQL>SET TERM OFF; SQL>BEGIN 2 IF :other_xml IS NULL THEN 3 RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).'); 4 END IF; 5 END; 6 / SQL>SET TERM OFF; Execute coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql on TARGET system in order to create a custom SQL Profile with plan 3617692013 linked to adjusted sql_text. COE_XFR_SQL_PROFILE completed.
從輸出可以看出,生成一個名為coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql的腳本。
b. 用coe_xfr_sql_profile.sql生成索引掃描SQL對應的腳本
SQL>@scripts/coe_xfr_sql_profile.sql Parameter 1: SQL_ID (required) Enter value for 1: 2ufquy7xs5nm5 PLAN_HASH_VALUE AVG_ET_SECS --------------- ----------- 1369807930 .001 Parameter 2: PLAN_HASH_VALUE (required) Enter value for 2: 1369807930 Values passed to coe_xfr_sql_profile: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SQL_ID : "2ufquy7xs5nm5" PLAN_HASH_VALUE: "1369807930" SQL>BEGIN 2 IF :sql_text IS NULL THEN 3 RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).'); 4 END IF; 5 END; 6 / SQL>SET TERM OFF; SQL>BEGIN 2 IF :other_xml IS NULL THEN 3 RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).'); 4 END IF; 5 END; 6 / SQL>SET TERM OFF; Execute coe_xfr_sql_profile_2ufquy7xs5nm5_1369807930.sql on TARGET system in order to create a custom SQL Profile with plan 1369807930 linked to adjusted sql_text. COE_XFR_SQL_PROFILE completed.
從輸出可以看出,生成一個名為coe_xfr_sql_profile_2ufquy7xs5nm5_1369807930.sql的腳本。
c. 把后生成的腳本里的Outline Data部分的Hint組合替換到先生成的腳本里,即下圖紅框部分內容
d. 執行coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql腳本
zx@MYDB>@coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql zx@MYDB>REM zx@MYDB>REM $Header: 215187.1 coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql 11.4.4.4 2017/02/28 carlos.sierra $ zx@MYDB>REM zx@MYDB>REM Copyright (c) 2000-2012, Oracle Corporation. All rights reserved. zx@MYDB>REM zx@MYDB>REM AUTHOR zx@MYDB>REM carlos.sierra@oracle.com zx@MYDB>REM zx@MYDB>REM SCRIPT zx@MYDB>REM coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql zx@MYDB>REM zx@MYDB>REM DESCRIPTION zx@MYDB>REM This script is generated by coe_xfr_sql_profile.sql zx@MYDB>REM It contains the SQL*Plus commands to create a custom zx@MYDB>REM SQL Profile for SQL_ID 6chcc0pvvhqqm based on plan hash zx@MYDB>REM value 3617692013. zx@MYDB>REM The custom SQL Profile to be created by this script zx@MYDB>REM will affect plans for SQL commands with signature zx@MYDB>REM matching the one for SQL Text below. zx@MYDB>REM Review SQL Text and adjust accordingly. zx@MYDB>REM zx@MYDB>REM PARAMETERS zx@MYDB>REM None. zx@MYDB>REM zx@MYDB>REM EXAMPLE zx@MYDB>REM SQL> START coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql; zx@MYDB>REM zx@MYDB>REM NOTES zx@MYDB>REM 1. Should be run as SYSTEM or SYSDBA. zx@MYDB>REM 2. User must have CREATE ANY SQL PROFILE privilege. zx@MYDB>REM 3. SOURCE and TARGET systems can be the same or similar. zx@MYDB>REM 4. To drop this custom SQL Profile after it has been created: zx@MYDB>REM EXEC DBMS_SQLTUNE.DROP_SQL_PROFILE('coe_6chcc0pvvhqqm_3617692013'); zx@MYDB>REM 5. Be aware that using DBMS_SQLTUNE requires a license zx@MYDB>REM for the Oracle Tuning Pack. zx@MYDB>REM 6. If you modified a SQL putting Hints in order to produce a desired zx@MYDB>REM Plan, you can remove the artifical Hints from SQL Text pieces below. zx@MYDB>REM By doing so you can create a custom SQL Profile for the original zx@MYDB>REM SQL but with the Plan captured from the modified SQL (with Hints). zx@MYDB>REM zx@MYDB>WHENEVER SQLERROR EXIT SQL.SQLCODE; zx@MYDB>REM zx@MYDB>VAR signature NUMBER; zx@MYDB>VAR signaturef NUMBER; zx@MYDB>REM zx@MYDB>DECLARE 2 sql_txt CLOB; 3 h SYS.SQLPROF_ATTR; 4 PROCEDURE wa (p_line IN VARCHAR2) IS 5 BEGIN 6 DBMS_LOB.WRITEAPPEND(sql_txt, LENGTH(p_line), p_line); 7 END wa; 8 BEGIN 9 DBMS_LOB.CREATETEMPORARY(sql_txt, TRUE); 10 DBMS_LOB.OPEN(sql_txt, DBMS_LOB.LOB_READWRITE); 11 -- SQL Text pieces below do not have to be of same length. 12 -- So if you edit SQL Text (i.e. removing temporary Hints), 13 -- there is no need to edit or re-align unmodified pieces. 14 wa(q'[select /*+no_index(t1 idx_t1) */ * from t1 where n=1 ]'); 15 DBMS_LOB.CLOSE(sql_txt); 16 h := SYS.SQLPROF_ATTR( 17 q'[BEGIN_OUTLINE_DATA]', 18 q'[IGNORE_OPTIM_EMBEDDED_HINTS]', 19 q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.1')]', 20 q'[DB_VERSION('11.2.0.1')]', 21 q'[ALL_ROWS]', 22 q'[OUTLINE_LEAF(@"SEL$1")]', 23 q'[INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))]', 24 q'[END_OUTLINE_DATA]'); 25 :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt); 26 :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE); 27 DBMS_SQLTUNE.IMPORT_SQL_PROFILE ( 28 sql_text => sql_txt, 29 profile => h, 30 name => 'coe_6chcc0pvvhqqm_3617692013', 31 description => 'coe 6chcc0pvvhqqm 3617692013 '||:signature||' '||:signaturef||'', 32 category => 'DEFAULT', 33 validate => TRUE, 34 replace => TRUE, 35 force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ ); 36 DBMS_LOB.FREETEMPORARY(sql_txt); 37 END; 38 / PL/SQL procedure successfully completed. zx@MYDB>WHENEVER SQLERROR CONTINUE zx@MYDB>SET ECHO OFF; SIGNATURE --------------------- 3589138201450662673 SIGNATUREF --------------------- 8068435081012723673 ... manual custom SQL Profile has been created COE_XFR_SQL_PROFILE_6chcc0pvvhqqm_3617692013 completed
e. 執行完成后再次查看目標SQL的執行計劃
zx@MYDB>select /*+no_index(t1 idx_t1) */ * from t1 where n=1; N ---------- 1
從執行計劃中可以看出已經走了INDEX RANGE SCAN,而且note部分提示SQL profile coe_6chcc0pvvhqqm_3617692013 used for this statement,說明執行sql時使用了該SQL Profile。
如果想在目標SQL的SQL文本發生變動時SQL Profile依然生效,則需要修改生成的腳本里的force_match=>true。
參考:《基于Oracle的SQL優化》
官方文檔:http://docs.oracle.com/cd/E11882_01/server.112/e41573/sql_tune.htm#PFGRF94854
http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_sqltun.htm#CHDGAJCI
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。