Oracle固定执行计划的方法----SQL Profie(三)
继续学习SQL Profile,SQL Profile的基本知识参考文章《Oracle固定执行计划的方法----SQL Profie(一)》、《Oracle固定执行计划的方法----SQL Profie(二)》
一、使用 COE_XFR_SQL_PROFILE 脚本构建SQL Profile------自动化利器
从 MOS(My Oracle Support)可以下载到 SQLT (SQLTXPLAIN) 工具包。其中包含的 coe_xfr_sql_profile.sql 脚本可以自动从 shared pool 或 AWR 中提取指定 SQL ID 的 Outline Data 并生成创建 SQL Profile 的脚本。
主要作用:如果一个SQL语句包含多条执行计划,且经常出现执行速度变化的情况,可以通过该脚本,选择一个预期的执行计划(当前内存中、AWR历史资料库中保存的执行计划),生成SQL Profile,来稳定该语句的执行计划!
备注:类似的脚本还有很多,例如create_sql_profile、create_sql_profile_awr,这里不一一介绍
1、下载与准备
- 从 MOS 下载
sqlt.zip文件(文档 ID: 215187.1)。 - 解压后,在 目录中找到
coe_xfr_sql_profile.sql脚本。
2、执行脚本
首先在 shared pool 中执行目标 SQL:
sql
SELECT /*+ sql_profile_test3 */ t1.*, t2.owner
FROM t1, t2
WHERE t1.object_name LIKE '%T1%'
AND t1.object_id = t2.object_id;
--注意需要查询 plan_hash_value 这一列
set linesize 200 pagesize 999
col sql_text format a40
SELECT sql_text, sql_id, hash_value, child_number,plan_hash_value
FROM v$sql
WHERE sql_text LIKE '%sql_profile_test3%'
AND sql_text NOT LIKE '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE
---------------------------------------- ------------- ---------- ------------ ---------------
SELECT /*+ sql_profile_test3 */ t1.*, t2 cdy6d6t5quzfd 1265466829 0 2959412835
.owner FROM t1, t2 WHERE t1.object_name
LIKE '%T1%' AND t1.object_id = t2.obje
ct_id
执行 coe_xfr_sql_profile.sql 脚本:
sql
@coe_xfr_sql_profile.sql
SQL> @coe_xfr_sql_profile.sql
Parameter 1:
SQL_ID (required)
Enter value for 1: cdy6d6t5quzfd ---输入sql_id
PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
2959412835 .033
Parameter 2:
PLAN_HASH_VALUE (required)
Enter value for 2: 2959412835 --输入语句想要生成sql profile的plan_hash_value
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID : "cdy6d6t5quzfd"
PLAN_HASH_VALUE: "2959412835"
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_cdy6d6t5quzfd_2959412835.sql --生成一个脚本文件,运行该脚本,就可以创建sql profile
on TARGET system in order to create a custom SQL Profile
with plan 2959412835 linked to adjusted sql_text.
COE_XFR_SQL_PROFILE completed.
脚本会生成另一个脚本 coe_xfr_sql_profile_cdy6d6t5quzfd_2959412835.sql。
3、执行生成的脚本
运行生成的脚本即可创建 SQL Profile:
sql
@coe_xfr_sql_profile_cdy6d6t5quzfd_2959412835.sql
SQL>@coe_xfr_sql_profile_cdy6d6t5quzfd_2959412835.sql
......
SQL>REM
SQL>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 /*+ sql_profile_test3 */ t1.*, t2.owner
15 FROM t1, t2
16 WHERE]');
17 wa(q'[ t1.object_name LIKE '%T1%'
18 AND t1.object_id = t2.object_id]');
19 DBMS_LOB.CLOSE(sql_txt);
20 h := SYS.SQLPROF_ATTR(
21 q'[BEGIN_OUTLINE_DATA]',
22 q'[IGNORE_OPTIM_EMBEDDED_HINTS]',
23 q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]',
24 q'[DB_VERSION('11.2.0.4')]',
25 q'[ALL_ROWS]',
26 q'[OUTLINE_LEAF(@"SEL$1")]',
27 q'[FULL(@"SEL$1" "T2"@"SEL$1")]',
28 q'[FULL(@"SEL$1" "T1"@"SEL$1")]',
29 q'[LEADING(@"SEL$1" "T2"@"SEL$1" "T1"@"SEL$1")]',
30 q'[USE_HASH(@"SEL$1" "T1"@"SEL$1")]',
31 q'[END_OUTLINE_DATA]');
32 :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);
33 :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);
34 DBMS_SQLTUNE.IMPORT_SQL_PROFILE (
35 sql_text => sql_txt,
36 profile => h,
37 name => 'coe_cdy6d6t5quzfd_2959412835',
38 description => 'coe cdy6d6t5quzfd 2959412835 '||:signature||' '||:signaturef||'',
39 category => 'DEFAULT',
40 validate => TRUE,
41 replace => TRUE,
42 force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );
43 DBMS_LOB.FREETEMPORARY(sql_txt);
44 END;
45 /
PL/SQL procedure successfully completed.
SQL>WHENEVER SQLERROR CONTINUE
SQL>SET ECHO OFF;
SIGNATURE
---------------------
10081132228680031145
SIGNATUREF
---------------------
7070267937381023371
... manual custom SQL Profile has been created
COE_XFR_SQL_PROFILE_cdy6d6t5quzfd_2959412835 completed
4、验证 SQL Profile 中的 Hints
sql
SELECT name, category, signature, type, status, force_matching
FROM dba_sql_profiles;
NAME CATEGORY SIGNATURE TYPE STATUS FOR
------------------------------ -------------- ---------- ------- -------- ---
MANUAL_PROFILE_T1_T2 DEFAULT 3.9607E+18 MANUAL ENABLED YES
coe_cdy6d6t5quzfd_2959412835 DEFAULT 1.0081E+19 MANUAL ENABLED NO
set linesize 200 pagesize 9999
set long 100000
SELECT COMP_DATA
FROM dba_sql_profiles a, sys.sqlobj$data b
WHERE a.signature = b.signature
AND a.name = 'coe_cdy6d6t5quzfd_2959412835';
COMP_DATA
--------------------------------------------------------------------------------
<outline_data><hint><![CDATA[BEGIN_OUTLINE_DATA]]></hint><hint><![CDATA[IGNORE_O
PTIM_EMBEDDED_HINTS]]></hint><hint><![CDATA[OPTIMIZER_FEATURES_ENABLE('11.2.0.4'
)]]></hint><hint><![CDATA[DB_VERSION('11.2.0.4')]]></hint><hint><![CDATA[ALL_ROW
S]]></hint><hint><![CDATA[OUTLINE_LEAF(@"SEL$1")]]></hint><hint><![CDATA[FULL(@"
SEL$1" "T2"@"SEL$1")]]></hint><hint><![CDATA[FULL(@"SEL$1" "T1"@"SEL$1")]]></hin
t><hint><![CDATA[LEADING(@"SEL$1" "T2"@"SEL$1" "T1"@"SEL$1")]]></hint><hint><![C
DATA[USE_HASH(@"SEL$1" "T1"@"SEL$1")]]></hint><hint><![CDATA[END_OUTLINE_DATA]]>
</hint></outline_data>
5、脚本的注意事项
- 脚本会从 shared pool 和 AWR 中获取 SQL 执行的各个执行计划的统计信息。
- 需要输入你认为正确的、需要稳定的执行计划的 hash value。
- 生成的 SQL Profile 名称格式为:
coe+sql_id+plan_hash_value。 force_match默认为 FALSE,可以手工修改生成的脚本将其改为 TRUE。- 也可以按需要修改生成脚本的其他内容。
二、高级技巧:在不修改 SQL 的情况下改变执行计划
在实际生产环境中,常常遇到这样的情况:SQL 语句的执行计划有问题,或者 SQL 使用了错误的 Hints(例如 /*+ RULE */),但应用代码不能修改或短期内无法修改。
有三种解决办法:
- 调整统计信息------不推荐,因为复杂、不稳定(统计信息可能被重新收集),且影响面广。
- 使用 Outline------方法复杂。
- 使用 SQL Profile------推荐方案。
1、解决思路:"乾坤大挪移"
使用 SQL Profile 改变 SQL 执行计划的核心思路是:
- 取得原始 SQL 的文本(如有可能还包括 SQL_ID)
- 通过 COE_XFR_SQL_PROFILE脚本生成构建sql profile的脚本。
- 构造一个与原始 SQL 在逻辑上、结构上完全相同的 SQL。
- 执行构造的 SQL,通过 COE_XFR_SQL_PROFILE脚本生成新的构建sql profile的脚本。
- 修改第二步生成的COE脚本内容修改(修改OUTLINE部分),使用第四步的COE脚本内容中的(OUTLINE DATA)。
2、案例演示
第一步:原始 SQL(待调整的执行计划)
sql
SELECT /*+ old_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*, t2.owner
FROM t1, t2
WHERE t1.object_name LIKE '%T1%'
AND t1.object_id = t2.object_id;
set linesize 200 pagesize 999
col sql_text format a40
SELECT sql_text, sql_id, hash_value, child_number,plan_hash_value
FROM v$sql
WHERE sql_text LIKE '%old_sql%'
AND sql_text NOT LIKE '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE
---------------------------------------- ------------- ---------- ------------ ---------------
SELECT /*+ old_sql full(t1) full(t2) use g7ndz4jp2j3uh 1781043024 0 2959412835
_hash(t1 t2) */ t1.*, t2.owner FROM t1,
t2 WHERE t1.object_name LIKE '%T1%' AN
D t1.object_id = t2.object_id
执行计划:
select * from table(dbms_xplan.display_cursor('g7ndz4jp2j3uh','',''));
Plan hash value: 2959412835
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | 5854 (100)| |
|* 1 | HASH JOIN | | 2500K| 97M| 1968K| 5854 (7)| 00:01:11 |
| 2 | TABLE ACCESS FULL| T2 | 87356 | 938K| | 344 (1)| 00:00:05 |
|* 3 | TABLE ACCESS FULL| T1 | 2500K| 71M| | 437 (84)| 00:00:06 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
3 - filter(("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT NULL))
原始 SQL 中使用了 FULL(t1)、FULL(t2) 和 USE_HASH(t1 t2) 这些 Hints,强制使用全表扫描 + 哈希连接。执行后 SQL_ID 为 g7ndz4jp2j3uh。
第二步:通过 COE_XFR_SQL_PROFILE脚本生成构建sql profile的脚本
SQL>@coe_xfr_sql_profile.sql
Parameter 1:
SQL_ID (required)
Enter value for 1: g7ndz4jp2j3uh
PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
2959412835 .057
Parameter 2:
PLAN_HASH_VALUE (required)
Enter value for 2: 2959412835
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID : "g7ndz4jp2j3uh"
PLAN_HASH_VALUE: "2959412835"
......
Execute coe_xfr_sql_profile_g7ndz4jp2j3uh_2959412835.sql
on TARGET system in order to create a custom SQL Profile
with plan 2959412835 linked to adjusted sql_text.
COE_XFR_SQL_PROFILE completed.
第三步:构造目标 SQL(我们希望的执行计划)
构造一条在逻辑上和结构上与原始 SQL 完全相同的 SQL,但使用我们希望的执行计划:
sql
SELECT /*+ new_sql index(t2) use_nl(t1 t2) */ t1.*, t2.owner
FROM t1, t2
WHERE t1.object_name LIKE '%T1%'
AND t1.object_id = t2.object_id;
set linesize 200 pagesize 999
col sql_text format a40
SELECT sql_text, sql_id, hash_value, child_number,plan_hash_value
FROM v$sql
WHERE sql_text LIKE '%new_sql%'
AND sql_text NOT LIKE '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE
---------------------------------------- ------------- ---------- ------------ ---------------
SELECT /*+ new_sql index(t2) use_nl(t1 t 0sz1znbny6v0m 3923995667 0 1386590592
2) */ t1.*, t2.owner FROM t1, t2 WHERE t
1.object_name LIKE '%T1%' AND t1.objec
t_id = t2.object_id
执行计划:
select * from table(dbms_xplan.display_cursor('0sz1znbny6v0m','',''));
Plan hash value: 1386590592
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5001K(100)| |
| 1 | NESTED LOOPS | | 2500K| 97M| 5001K (1)| 16:40:18 |
| 2 | NESTED LOOPS | | 2500K| 97M| 5001K (1)| 16:40:18 |
|* 3 | TABLE ACCESS FULL | T1 | 2500K| 71M| 437 (84)| 00:00:06 |
|* 4 | INDEX RANGE SCAN | IND_T2 | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT NULL))
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
第四步:通过 COE_XFR_SQL_PROFILE脚本,提取构造 SQL 的SQL Profile
使用 coe_xfr_sql_profile.sql 脚本提取构造 SQL 的 Outline Data:
sql
@coe_xfr_sql_profile.sql
-- 输入构造 SQL 的 SQL_ID(0sz1znbny6v0m)和 PLAN_HASH_VALUE(1386590592)
-- /*+ new_sql index(t2) use_nl(t1 t2) */SQL 的 Outline Data
SQL>@coe_xfr_sql_profile.sql
Parameter 1:
SQL_ID (required)
Enter value for 1: 0sz1znbny6v0m
PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
1386590592 .033
Parameter 2:
PLAN_HASH_VALUE (required)
Enter value for 2: 1386590592
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID : "0sz1znbny6v0m"
PLAN_HASH_VALUE: "1386590592"
......
SQL>SET TERM OFF;
Execute coe_xfr_sql_profile_0sz1znbny6v0m_1386590592.sql
on TARGET system in order to create a custom SQL Profile
with plan 1386590592 linked to adjusted sql_text.
COE_XFR_SQL_PROFILE completed.
生成的脚本 coe_xfr_sql_profile_0sz1znbny6v0m_1386590592.sql 中包含 Outline Data:
sql
h := SYS.SQLPROF_ATTR(
q'[BEGIN_OUTLINE_DATA]',
q'[IGNORE_OPTIM_EMBEDDED_HINTS]',
q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]',
q'[DB_VERSION('11.2.0.4')]',
q'[ALL_ROWS]',
q'[OUTLINE_LEAF(@"SEL$1")]',
q'[FULL(@"SEL$1" "T1"@"SEL$1")]',
q'[INDEX(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))]',
q'[LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")]',
q'[USE_NL(@"SEL$1" "T2"@"SEL$1")]',
q'[NLJ_BATCHING(@"SEL$1" "T2"@"SEL$1")]',
q'[END_OUTLINE_DATA]');
第五步:修改第二步的脚本内容
针对原始 SQL(SQL_ID = g7ndz4jp2j3uh,PLAN_HASH_VALUE = 2959412835)使用 coe_xfr_sql_profile.sql 生成脚本。
然后手工修改 生成的脚本 coe_xfr_sql_profile_g7ndz4jp2j3uh_2959412835.sql:
-
将
h := SYS.SQLPROF_ATTR(...)那段替换成第四步得到的 Outline Data。q'[BEGIN_OUTLINE_DATA]', q'[IGNORE_OPTIM_EMBEDDED_HINTS]', q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]', q'[DB_VERSION('11.2.0.4')]', q'[ALL_ROWS]', q'[OUTLINE_LEAF(@"SEL$1")]', q'[FULL(@"SEL$1" "T2"@"SEL$1")]', q'[FULL(@"SEL$1" "T1"@"SEL$1")]', q'[LEADING(@"SEL$1" "T2"@"SEL$1" "T1"@"SEL$1")]', q'[USE_HASH(@"SEL$1" "T1"@"SEL$1")]', q'[END_OUTLINE_DATA]'); 改为 q'[BEGIN_OUTLINE_DATA]', q'[IGNORE_OPTIM_EMBEDDED_HINTS]', q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]', q'[DB_VERSION('11.2.0.4')]', q'[ALL_ROWS]', q'[OUTLINE_LEAF(@"SEL$1")]', q'[FULL(@"SEL$1" "T1"@"SEL$1")]', q'[INDEX(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))]', q'[LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")]', q'[USE_NL(@"SEL$1" "T2"@"SEL$1")]', q'[NLJ_BATCHING(@"SEL$1" "T2"@"SEL$1")]', q'[END_OUTLINE_DATA]'); -
将
force_match从FALSE改为TRUE。
第六步:执行修改后的脚本
sql
@@coe_xfr_sql_profile_g7ndz4jp2j3uh_2959412835.sql
PL/SQL 过程已成功完成。
SIGNATURE
---------------------
15211772502623681692
SIGNATUREF
---------------------
1099879712749910124
... manual custom SQL Profile has been created
COE_XFR_SQL_PROFILE_g7ndz4jp2j3uh_2959412835 completed
第七步:验证效果
执行原始 SQL:
sql
SELECT /*+ old_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*, t2.owner
FROM t1, t2
WHERE t1.object_name LIKE '%T1%'
AND t1.object_id = t2.object_id;
set linesize 200 pagesize 999
col sql_text format a40
SELECT sql_text, sql_id, hash_value, child_number,plan_hash_value
2 FROM v$sql
3 WHERE sql_text LIKE '%old_sql%'
4 AND sql_text NOT LIKE '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE
---------------------------------------- ------------- ---------- ------------ ---------------
SELECT /*+ old_sql full(t1) full(t2) use g7ndz4jp2j3uh 1781043024 0 1386590592
_hash(t1 t2) */ t1.*, t2.owner FROM t1,
t2 WHERE t1.object_name LIKE '%T1%' AN
D t1.object_id = t2.object_id
执行计划:
SQL>select * from table(dbms_xplan.display_cursor('g7ndz4jp2j3uh','',''));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
SQL_ID g7ndz4jp2j3uh, child number 0
-------------------------------------
SELECT /*+ old_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*, t2.owner
FROM t1, t2 WHERE t1.object_name LIKE '%T1%' AND t1.object_id =
t2.object_id
Plan hash value: 1386590592
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5001K(100)| |
| 1 | NESTED LOOPS | | 2500K| 97M| 5001K (1)| 16:40:18 |
| 2 | NESTED LOOPS | | 2500K| 97M| 5001K (1)| 16:40:18 |
|* 3 | TABLE ACCESS FULL | T1 | 2500K| 71M| 437 (84)| 00:00:06 |
|* 4 | INDEX RANGE SCAN | IND_T2 | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT NULL))
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Note
-----
- SQL profile coe_g7ndz4jp2j3uh_2959412835 used for this statement
成功! SQL Profile 起作用了,执行计划从全表扫描 + 哈希连接变成了嵌套循环连接 + 索引范围扫描。