Undocumented Way–通過手工創建sql profiles固定執行計劃進行SQL調優

 

         前幾天在查閱新書<Pro Oracle SQL>介紹的時候,看到作者blog上有提出了undocmented way手工構造SQL profile的例子,確實能像outline一樣,強制固定住執行計劃,這頓時讓我覺得柳暗花明,因爲我之前一直對SQL profiles的穩定性大傷腦筋。結合最近的幾次項目調優,使用這種辦法固定SQL計劃,效果相當不錯。

        手工構造SQL Profiles,可以使用oracle提供的dbms_sqltune.import_sql_profile過程。

SQL> desc dbms_sqltune.import_sql_profile
Parameter   Type             Mode Default?
———– —————- —- ——–
SQL_TEXT    CLOB             IN             —-sql文本
PROFILE     SYS.SQLPROF_ATTR IN      —-profile info
NAME        VARCHAR2         IN   Y     —profile名稱
DESCRIPTION VARCHAR2         IN   Y
CATEGORY    VARCHAR2         IN   Y   —類別
VALIDATE    BOOLEAN          IN   Y
REPLACE     BOOLEAN          IN   Y
FORCE_MATCH BOOLEAN          IN   Y     –是否對sql語句進行匹配,遊標共享。

         其中Profile參數,我們可以按照既定格式手工填充,因爲一般要調優的SQL語句相對比較長,如果手工填充,不是很現實,可以用已生成的執行計劃,從v$sql_plan中提取(當然也可以從awr的dba_hist_sql_plan中提取,提取方法可參見後面腳本)

          從v$sql_plan中提取sql profiles信息: create_sql_cursor_profiles.sql(該腳本基於Kerry Osborne完善而成,增加了SQL Profile信息調換功能,更方便的爲我們選擇想要的執行計劃)

          以下是一個調優案例:

          項目中有個模塊需要進行日結報表統計,沒有使用綁定變量,應用返回超時,涉及語句和生產執行計劃如下:

select a.*
b.ofr_create_time as FILE_CREATETIME,
b.ofr_file_name as FILE_name,
b.ofr_completetime as FILE_RECRONTIME,
b.ofr_deal_type as DEAL_TYPE,
c.ubr_last_send as UBR_LAST_SEND,
c.ubr_completetime as UBR_COMPLETETIME,
c.ubr_state as UBR_STATE,
d.ofc_reckonaccount as ofc_reckonaccount,
d.ofc_province as ofc_province
from UNP_PAYRECORD a, unp_orafile b, unp_paybatch c, unp_orarecord d
where a.ubc_ofc_id = d.ofc_id
and a.ubc_ofc_sn = d.ofc_sn
and a.ubc_id = c.ubr_id
and a.ubc_ofc_id = b.ofr_id
and b.ofr_deal_type = ’101003′
and c.ubr_last_send between to_date(’2010-11-12′, ‘yyyy-mm-dd’) and
to_date(’2010-11-12′, ‘yyyy-mm-dd’) + 1
and b.OFR_COMPLETETIME is not null
and a.UBC_OFC_PAY_CHANNEL = ’010501′
order by a.ubc_id desc, a.ubc_sn asc
——————————————————————————–
| Id  | Operation                       | Name          | Rows  | Bytes |TempSpc
——————————————————————————–
|   0 | SELECT STATEMENT                |               |       |       |
|   1 |  SORT ORDER BY                  |               | 93172 |    27M|    58M
|*  2 |   HASH JOIN                     |               | 93172 |    27M|    23M
|*  3 |    HASH JOIN                    |               | 92549 |    21M|    21M
|*  4 |     HASH JOIN                   |               | 93486 |    20M|  3656K
|   5 |      TABLE ACCESS BY INDEX ROWID| UNP_PAYBATCH  | 87005 |  2633K|
|*  6 |       INDEX RANGE SCAN          | IDX_LAST_SEND | 87005 |       |
|*  7 |      TABLE ACCESS FULL          | UNP_PAYRECORD |  5468K|  1053M|
|   8 |     TABLE ACCESS FULL           | UNP_ORARECORD |    21M|   322M|
|*  9 |    TABLE ACCESS FULL            | UNP_ORAFILE   |   451K|    23M|
——————————————————————————–

           通過添加Hints,我們認爲如下優化後的語句和執行計劃可以被接受:

select /*+  ordered use_nl(a,b,c,d) */ a.*,
b.ofr_create_time as FILE_CREATETIME,
b.ofr_file_name as FILE_NAME,
b.ofr_completetime as FILE_RECRONTIME,
b.ofr_deal_type as DEAL_TYPE,
c.ubr_last_send as UBR_LAST_SEND,
c.ubr_completetime as UBR_COMPLETETIME,
c.ubr_state as UBR_STATE,
d.ofc_reckonaccount as ofc_reckonaccount,
d.ofc_province as ofc_province
from   unp_paybatch c,UNP_PAYRECORD a ,unp_orafile b,unp_orarecord d
where a.ubc_ofc_id = d.ofc_id
and a.ubc_ofc_sn = d.ofc_sn
and a.ubc_id = c.ubr_id
and a.ubc_ofc_id = b.ofr_id
and b.ofr_deal_type = ’101003′
and c.ubr_last_send between to_date(’2010-11-12′, ‘yyyy-mm-dd’) and
to_date(’2010-11-12′, ‘yyyy-mm-dd’) + 1
and b.OFR_COMPLETETIME is not null
and a.UBC_OFC_PAY_CHANNEL = ’010501′
order by a.ubc_id desc, a.ubc_sn asc

——————————————————————————–
| Id  | Operation                       | Name          | Rows  | Bytes |TempSpc
——————————————————————————–
|   0 | SELECT STATEMENT                |               |       |       |
|   1 |  SORT ORDER BY                  |               | 93172 |    27M|    58M
|   2 |   NESTED LOOPS                  |               | 93172 |    27M|
|   3 |    NESTED LOOPS                 |               | 94116 |    25M|
|   4 |     NESTED LOOPS                |               | 93486 |    20M|
|   5 |      TABLE ACCESS BY INDEX ROWID| UNP_PAYBATCH  | 87005 |  2633K|
|*  6 |       INDEX RANGE SCAN          | IDX_LAST_SEND | 87005 |       |
|*  7 |      TABLE ACCESS BY INDEX ROWID| UNP_PAYRECORD |     1 |   202 |
|*  8 |       INDEX RANGE SCAN          | SYS_C003463   |     1 |       |
|*  9 |     TABLE ACCESS BY INDEX ROWID | UNP_ORAFILE   |     1 |    55 |
|* 10 |      INDEX UNIQUE SCAN          | SYS_C003412   |     1 |       |
|  11 |    TABLE ACCESS BY INDEX ROWID  | UNP_ORARECORD |     1 |    16 |
|* 12 |     INDEX UNIQUE SCAN           | SYS_C003427   |     1 |       |
——————————————————————————–

          因爲該語句有3個傳入參數,沒有使用綁定變量,因此不能通過outline的方式進行SQL語句固定,而且,由於亞運期間運維保證封網,無法進行前臺應用程序的修改。因此只能尋找別的數據庫層上的途徑。

         嘗試去手工去構造SQL Profile。用的就是偷樑換柱的思想,將原始的SQL語句的profiles和優化後的SQL語句的profiles信息調換(這種辦法,也常常用在outline的構造中)

         查詢到原始的SQL語句,其sql_id爲’ar6qss94q5m9b’,child_number爲0

         加過Hints優化後的SQL語句,其sql_id爲’4brxrmxzhavtr’,child_number爲0

         生成SQL profile,其sql文本對應爲ar6qss94q5m9b的sqltext(原始文本),而執行計劃信息對應爲4brxrmxzhavtr的執行計劃(調優後的計劃)。

SQL> @create_sql_cursor_profiles.sql
Enter value for sql_id1(used to generate sql_text): ar6qss94q5m9b
Enter value for child_no1 (used to generate sql_text) (0): 0
Enter value for sql_id2(used to generate sql_hints): 4brxrmxzhavtr
Enter value for child_no2(used to generate sql_hints) (0):
Enter value for profile_name (PROF_sqlid_planhash): PROF_1
Enter value for category (DEFAULT):
Enter value for force_matching (FALSE): true
old   9: decode(‘&&profile_name’,'X0X0X0X0′,’PROF_&&sql_id1′||’_'||plan_hash_value,’&&profile_name’)
new   9: decode(‘X0X0X0X0′,’X0X0X0X0′,’PROF_ar6qss94q5m9b’||’_'||plan_hash_value,’X0X0X0X0′)
old  15: sql_id = ‘&&sql_id1′
new  15: sql_id = ‘ar6qss94q5m9b’
old  16: and child_number = &&child_no1;
new  16: and child_number = 0;
old  31: sql_id = ‘&&sql_id2′
new  31: sql_id = ’4brxrmxzhavtr’
old  32: and child_number = &&child_no2
new  32: and child_number = 0
old  40: category => ‘&&category’,
new  40: category => ‘DEFAULT’,
old  42: force_match => &&force_matching
new  42: force_match => true
old  53:   dbms_output.put_line(‘ERROR: sql_id: ‘||’&&sql_id1′||’ Child: ‘||’&&child_no1′||’ not found in v$sql.’);
new  53:   dbms_output.put_line(‘ERROR: sql_id: ‘||’ar6qss94q5m9b’||’ Child: ‘||’0′||’ not found in v$sql.’);

PL/SQL procedure successfully completed.

          查看是否有SQL profile生成.

SQL> select name,category,signature,sql_text,created,status,force_matching from dba_sql_profiles;
NAME       CATEGORY                        SIGNATURE SQL_TEXT                     CREATED     STATUS   FORCE_MATCHING
—————————— —————————— ———- —————————————————————–
PROF_1       DEFAULT                        8.70867244        (略)                        2010/12/20  ENABLED                       YES

         查看該PROF_1的outline hints

SQL> select attr_val  from dba_sql_profiles p, sys.sqlprof$attr h where p.signature = h.signature  and p.category = h.category and a.name=’PROF_1′;

IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE(’10.2.0.4′)
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "C"@"SEL$1" ("UNP_PAYBATCH"."UBR_LAST_SEND"))
INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("UNP_PAYRECORD"."UBC_ID" "UNP_PAYRECORD"."UBC_SN"))
INDEX_RS_ASC(@"SEL$1" "B"@"SEL$1" ("UNP_ORAFILE"."OFR_ID"))
INDEX_RS_ASC(@"SEL$1" "D"@"SEL$1" ("UNP_ORARECORD"."OFC_ID" "UNP_ORARECORD"."OFC_SN"))
LEADING(@"SEL$1" "C"@"SEL$1" "A"@"SEL$1" "B"@"SEL$1" "D"@"SEL$1")
USE_NL(@"SEL$1" "A"@"SEL$1")
USE_NL(@"SEL$1" "B"@"SEL$1")
USE_NL(@"SEL$1" "D"@"SEL$1")

           可見,該語句的SQL Profile已經生成,來驗證一下這個語句現在使用的執行計劃。

SQL> select * from table(dbms_xplan.display_cursor(‘ar6qss94q5m9b’));
PLAN_TABLE_OUTPUT
——————————————————————————–
SQL_ID  ar6qss94q5m9b, child number 0
————————————-
select a.*,        b.ofr_create_time as FILE_CREATETIME,        b.ofr_file_name
b.ofr_completetime as FILE_RECRONTIME,        b.ofr_deal_type as DEAL_TYPE,
c.ubr_last_send as UBR_LAST_SEND,        c.ubr_completetime as UBR_COMPLETETIME,
c.ubr_state as UBR_STATE,        d.ofc_reckonaccount as ofc_reckonaccount,
as ofc_province   from UNP_PAYRECORD a, unp_orafile b, unp_paybatch c, unp_orare
a.ubc_ofc_id = d.ofc_id    and a.ubc_ofc_sn = d.ofc_sn    and a.ubc_id = c.ubr_i
a.ubc_ofc_id = b.ofr_id    and b.ofr_deal_type = ’101003′    and c.ubr_last_send
to_date(’2010-11-12′, ‘yyyy-mm-dd’) and        to_date(’2010-11-12′, ‘yyyy-mm-dd
b.OFR_COMPLETETIME is not null    and a.UBC_OFC_PAY_CHANNEL = ’010501′  order by
a.ubc_sn asc
Plan hash value: 724325605
——————————————————————————–
| Id  | Operation                       | Name          | Rows  | Bytes |TempSpc
——————————————————————————–
|   0 | SELECT STATEMENT                |               |       |       |
|   1 |  SORT ORDER BY                   |               | 93172 |    27M|    58M
|   2 |   NESTED LOOPS                  |               | 93172 |    27M|
|   3 |    NESTED LOOPS                 |               | 94116 |    25M|
|   4 |     NESTED LOOPS                |               | 93486 |    20M|
|   5 |      TABLE ACCESS BY INDEX ROWID| UNP_PAYBATCH  | 87005 |  2633K|
|*  6 |       INDEX RANGE SCAN          | IDX_LAST_SEND | 87005 |       |
|*  7 |      TABLE ACCESS BY INDEX ROWID| UNP_PAYRECORD |     1 |   202 |
|*  8 |       INDEX RANGE SCAN          | SYS_C003463   |     1 |       |
|*  9 |     TABLE ACCESS BY INDEX ROWID | UNP_ORAFILE   |     1 |    55 |
|* 10 |      INDEX UNIQUE SCAN          | SYS_C003412   |     1 |       |
|  11 |    TABLE ACCESS BY INDEX ROWID  | UNP_ORARECORD |     1 |    16 |
|* 12 |     INDEX UNIQUE SCAN           | SYS_C003427   |     1 |       |
——————————————————————————–
Predicate Information (identified by operation id):
—————————————————
6 – access("C"."UBR_LAST_SEND">=TIMESTAMP’ 2010-11-12 00:00:00′ AND
"C"."UBR_LAST_SEND"<=TIMESTAMP’ 2010-11-13 00:00:00′)
7 – filter("A"."UBC_OFC_PAY_CHANNEL"=’010501′)
8 – access("A"."UBC_ID"="C"."UBR_ID")
9 – filter(("B"."OFR_DEAL_TYPE"=’101003′ AND "B"."OFR_COMPLETETIME" IS NOT NU
PLAN_TABLE_OUTPUT
——————————————————————————–
10 – access("A"."UBC_OFC_ID"="B"."OFR_ID")
12 – access("A"."UBC_OFC_ID"="D"."OFC_ID" AND "A"."UBC_OFC_SN"="D"."OFC_SN")
Note
—–
- SQL profile "PROF_1" used for this statement

        執行計劃與我們預期的一致,由於SQL Profile的設置了force match,因此,可以替入其他參數值,驗證執行計劃依然是完美保持的,對執行計劃的固定原理上,同outline也就沒有太大區別了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章