之前写了一篇文章介绍的是用SQL Profile来调整、稳定目标SQL的执行计划,即使无法修改目标SQL的SQL文本。但SQL Profile实际上只是一种亡羊补牢、被动的技术手段,应用在那些执行计划已经发生了不好的变更的SQL上,即当我们发现这些SQL的执行计划已经出了问题时通过创建SQL Profile来纠正、稳定这些SQL的执行计划。即便通过创建SQL Profile解决了目标SQL执行计划变更的问题,依然不能保证系统后续执行的SQL的执行计划就不再发生不好的变更。这种不确定性会给Oracle数据库大版本升级(比如从Oracle 10g升级到Oracle 11g)带来一系列的麻烦,因为不清楚升级之后原先系统中哪些SQL的执行计划会发生不好的变更。
为了解决上述问题,Oracle在11g中推出了SPM(SQL Plan Management)。SPM是一种主动的稳定执行计划的手段,能够保证只有被验证过的执行计划才会被启用,当由于种种原因(如统计信息的变更)而导致目标SQL产生了新的执行计划后,这个新的执行计划并不会被马上启用,直到它已经被我们验证过其执行效率会比原先执行计划高才会被启用。
随着Oracle数据库版本的不段推进,其CBO的算法、功能也在一直不断进化和增加,所以同样的SQL有可能在新版本的Oralce数据库中执行效率更高,如果我们使用了SQL Profile(特别是使用了Manual类型的SQL Profile)来稳定目标SQL的执行计划,那就意味着可能失去了继续优化上述SQL的执行效率的机会。而SPM的推出可以说彻底解决了执行计划稳定性的问题,它既能主动地稳定执行计划,又保留了继续使用新的执行效率可能更高的执行计划的机会。
当启用了SPM后,每一个SQL都会存在对应的SQL Plan Baseline,这个SQL Plan Baseline里存储的就是该SQL的执行计划,如果一个SQL有多个执行计划,那么该SQL就可能会有多个SQL Plan Baseline,可以从DBA_SQL_PLAN_BASELINES中查看目标SQL所有的SQL Plan Baseline。
DBA_SQL_PLAN_BASELINES中的列ENABLED和ACCEPTED用来描述一个SQL Plan Baseline所对应的执行计划是否能被Oracle启用,只有ENABLED和ACCEPTED的值均为“YES”的SQL Plan Baseline所对应的执行计划才会被Oracle启用,如果一具SQL有超过1个以上的SQL Plan Baseline的ENABLED和ACCEPTED的值均为YES,则Oracle会从中选择成本值最小的一个所对应的执行坟墓来作为该SQL的执行计划。
在Oracle 11g及其以上的版本中,有如下两种方法可以产生目标SQL的SQL Plan Baseline。
自动捕获
手工生成/批量导入(批量导入尤其适用于Oracle数据库大版本的升级,它可以确保升级后原有系统所胡SQL的执行计划不会发生变化)
下面分别介绍如何自动捕获和手工的方式来产生SQL Plan Baseline。
1 自动捕获SQL Plan Baseline
参数OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES用于控制是否开启自动捕获SQL Plan Baseline,其默认值为FALSE,表示在默认情况下,Oracle并不会自动捕获SQL Plan Baseline。这个参数可以在session或系统级别动态修改。当修改为TRUE后,则Oracle会对上述参数影响范围内所有重复执行的SQL自动捕获其SQL Plan Baseline,并且针对目标SQL第一次捕获的SQL Plan Baseline的ENABLED和ACCEPTED的值均为“YES”。随后如果该SQL的执行计划发生了变更,则再次捕获到的SQL Plan Baseline的ENABLED的值依然为YES,但ACCEPTED的值变为了NO,这表示后续变更的执行计划虽然被捕获了,但Oracle不会将其作为该SQL的执行计划来执行,即此时Oracle会永远沿用该SQL第一次被捕获的SQL Plan Baseline所对应的执行计划(除非后续做了手工调整)。
参数OPTIMIZER_USE_SQL_PLAN_BASELINES用于控制是否启用SQL Plan Baseline,其默认值为TRUE,表示在默认情况下,Oracle在生成执行计划时就会启用SPM,使用已有的SQL Plan Baseline,这个参数也可以在session或系统级别动态修改。
下面看一下实例:
查看上述两个参数的默认值
zx@MYDB>show parameter sql_plan
NAME TYPE VALUE
------------------------------------ --------------------------------- ------------------------------
optimizer_capture_sql_plan_baselines boolean FALSE
optimizer_use_sql_plan_baselines boolean TRUE
在当前session中禁掉SPM并同时开启自动捕获SQL Plan Baseline:
zx@MYDB>alter session set optimizer_use_sql_plan_baselines=FALSE;
Session altered.
zx@MYDB>alter session set optimizer_capture_sql_plan_baselines=TRUE;
Session altered.
创建测试表T2
zx@MYDB>create table t2 as select * from dba_objects;
Table created.
zx@MYDB>create index idx_t2 on t2(object_id);
Index created.
zx@MYDB>exec dbms_stats.gather_table_stats(ownname=>USER,tabname=>'T2',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
zx@MYDB>select object_id,object_name from t2 where object_id between 103 and 108;
OBJECT_ID OBJECT_NAME
---------- ------------------------------
103 MIGRATE$
104 DEPENDENCY$
105 ACCESS$
106 I_DEPENDENCY1
107 I_DEPENDENCY2
108 I_ACCESS1
6 rows selected.