我们当然希望这样的改变永远不要发生,即在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 Profile可以很容易实现如下两个目的:
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来调整执行计划的实例:
创建测试表及相关操作:
从上述显示内容可以看出,目标SQL走的是对表T1的全表扫描(Table Access Full),这个执行计划显然是错误,这里正确的执行坟墓应该是走索引IDX_T1的索引范围扫描(Index Range Scan)。下面使用SQL Tuning Advisor对这条SQL生成Automatic类型的SQL Profile。
a.先创建一个名为my_sql_tuning_task_2的自动调整任务:
注:创建任务时可以使用SQL来创建,可以适用于SQL文本长的情况。详情参考官方文档。
b.执行上述自动调整任务
c.查看上述自动任务的调整结果
从上述调整结果可以看到,Oracle现在告诉我们:它已经为目标SQL找到了更好的执行计划,并且已经创建了针对该SQL的Automatic类型的SQL Profile。如果我们使用accecp_sql_profile接受了这个SQL Profile,则目标SQL的响应时间将会有86.24%的提升,逻辑读将会有95%的提升,并且接受了该SQL Profile后目标SQL的执行计划将会由原来的全表扫描变为索引范围扫描。
上面Automatic类型的SQL Profile所产生的调整结果就是我们想要的,所以现在只需按Oracle的提示接受这个SQL Profile即可:
接受此SQL Profile后我们来看一下效果,再次执行目标SQL:
注意到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
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
从上述输出可以看出执行计划仍然走全表扫描。
现在来创建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,强制走索引:
从执行计划中可以看出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对应的脚本
从输出可以看出,生成一个名为coe_xfr_sql_profile_6chcc0pvvhqqm_3617692013.sql的脚本。
b.?用coe_xfr_sql_profile.sql生成索引扫描SQL对应的脚本
从输出可以看出,