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也就没有太大区别了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值