共计 19820 个字符,预计需要花费 50 分钟才能阅读完成。
之前写了一篇文章介绍的是用 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.
从执行计划上看,走的是索引 IDX_T2 上的索引范围扫描,因为 SQL 只执行了一次,所以 Oracle 不会自动捕获 SQL Plan Baseline,DBA_SQL_PLAN_BASELINES 中没有记录
zx@MYDB>col sql_handle
for
a30
zx@MYDB>col plan_name
for
a30
zx@MYDB>col origin
for
a20
zx@MYDB>col sql_text
for
a70
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines;
no
rows
selected
再次执行上述 SQL,因为重复执行该 SQL,Oracle 自动捕获了这个 SQL 的 SQL Plan Baseline
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.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ------------------------------------------------------------
SYS_SQL_ac526b1e4be74880 SQL_PLAN_asnmb3t5yfk4024c6dbb6 AUTO-CAPTURE YES YES
select
object_id,object_name
from
t2
where
object_id
between
103
and
108
现在将索引 IDX_T2 的聚簇因子修改为 2400 万,目的是为了能让 SQL 的执行计划变为对表 T2 的全表扫描(为何修改聚簇因子,参考 http://www.linuxidc.com/Linux/2017-02/141071.htm)。修改完后再执行上述 SQL,并查看执行计划:
zx@MYDB>
exec
dbms_stats.set_index_stats(ownname=>
USER
,indname=>
'IDX_T2'
,clstfct=>24000000,no_invalidate=>
false
);
PL/SQL
procedure
successfully completed.
zx@MYDB>
select
index_name,clustering_factor
from
dba_indexes
where
index_name=
'IDX_T2'
;
INDEX_NAME CLUSTERING_FACTOR
------------------------------------------------------------------------------------------ -----------------
IDX_T2 24000000
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.
从执行计划中可以看出该 SQL 的执行计划已经变为全表扫描。因为目标 SQL 已经重复执行且同时又产生了一个新的执行计划,所以现在 Oracle 就会自动捕获并创建这个新的执行计划所对应的 SQL Plan Baseline 了。从如下查询可以看出 Oracle 对新的执行计划产生了一个新的 SQL Plan Baseline,其 ENABLED 的值依然为 YES,但 ACCEPTED 的值变为了 NO:
现在我们对当前 Session 关闭自动捕获 SQL Plan Baseline 并同时开启 SPM,现在索引 IDX_T2 的聚簇因子依然为 2400 万,再次执行目标 SQL,并查看执行计划:
zx@MYDB>
alter
session
set
optimizer_use_sql_plan_baselines=
TRUE
;
Session altered.
zx@MYDB>
alter
session
set
optimizer_capture_sql_plan_baselines=
FALSE
;
Session altered.
zx@MYDB>
select
index_name,clustering_factor
from
dba_indexes
where
index_name=
'IDX_T2'
;
INDEX_NAME CLUSTERING_FACTOR
------------------------------------------------------------------------------------------ -----------------
IDX_T2 24000000
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.
从上面的显示内容可以看出,现在目标 SQL 的执行又从全表扫描恢复为了索引范围扫描,并且执行计划中的 Note 部分有“SQL plan baseline SQL_PLAN_asnmb3t5yfk4024c6dbb6 used for this statement”内容,说明 SPM 开启的情况下,即便目标 SQL 产生了新的执行计划,Oracle 依然只会应用该 SQL 的 ENABLED 和 ACCEPTED 的值均为 YES 的 SQL Plan Baselline。
如果想启用目标 SQL 新的执行计划(即全表扫描),应该如何做呢?
针对不同的 Oracle 版本,会有不同的处理方法。比如这里想启用目标 SQL 的新的执行计划,如果是 11gR1 的环境,则只需要将目标 SQL 所采用的名为 SQL_PLAN_asnmb3t5yfk4024c6dbb6 的 SQL Plan Baseline(即索引范围扫描)的 ACCEPTED 的值设为 NO 就可以了。但对于 11gR2 环境,上述方法会报错,因为在 11gR2 中,所有已经被 ACCEPTED 的 SQL Plan Baseline 的 ACCEPTED 的值将不再能够被设为 NO:
zx@MYDB>var
temp
varchar2(1000);
zx@MYDB>
exec
:
temp
:= dbms_spm.alter_sql_plan_baseline(sql_handle=>
'SYS_SQL_ac526b1e4be74880'
,plan_name=>
'SQL_PLAN_asnmb3t5yfk4024c6dbb6'
,attribute_name=>
'accepted'
,attribute_value=>
'NO'
);
BEGIN
:
temp
:= dbms_spm.alter_sql_plan_baseline(sql_handle=>
'SYS_SQL_ac526b1e4be74880'
,plan_name=>
'SQL_PLAN_asnmb3t5yfk4024c6dbb6'
,attribute_name=>
'accepted'
,attribute_value=>
'NO'
);
END
;
*
ERROR
at
line 1:
ORA-38136: invalid attribute
name
ACCEPTED specified
ORA-06512:
at
"SYS.DBMS_SPM"
, line 2469
ORA-06512:
at
line 1
在 11gR2 中,我们可以联合使用 DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE 和 DBMS_SPM.ALTER_SQL_PLAN_BASELINE 达到启用目标 SQL 新的执行计划的目的。
先用 DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE 将新的执行计划 (全表扫描) 所对应的 SQL Plan Baseline 的 ACCEPTED 值设为“YES”:
zx@MYDB>
exec
:
temp
:= dbms_spm.evolve_sql_plan_baseline(sql_handle=>
'SYS_SQL_ac526b1e4be74880'
,plan_name=>
'SQL_PLAN_asnmb3t5yfk40b860bcf2'
,verify=>
'NO'
,
commit
=>
'YES'
);
PL/SQL
procedure
successfully completed.
从上面显示的内容看到如下信息:“Plan: SQL_PLAN_asnmb3t5yfk40b860bcf2—-Plan was changed to an accepted plan.”,这表明已经将新的执行计划 (全表扫描) 所对应的 SQL Plan Baseline 的 ACCEPTED 值设为 YES
从下面的查询结果也可以证明:
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select object_id%'
;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ----------------------------------------------------------------------
SYS_SQL_ac526b1e4be74880 SQL_PLAN_asnmb3t5yfk4024c6dbb6 AUTO-CAPTURE YES YES
select
object_id,object_name
from
t2
where
object_id
between
103
and
108
SYS_SQL_ac526b1e4be74880 SQL_PLAN_asnmb3t5yfk40b860bcf2 AUTO-CAPTURE YES YES
select
object_id,object_name
from
t2
where
object_id
between
103
and
108
然后再使用 DBMS_SPM.ALTER_SQL_PLAN_BASELINE 将原先的执行计划 (索引范围扫描) 对应的 SQL Plan Baseline 的 ENABLED 的值设为 NO:
zx@MYDB>
exec
:
temp
:= dbms_spm.alter_sql_plan_baseline(sql_handle=>
'SYS_SQL_ac526b1e4be74880'
,plan_name=>
'SQL_PLAN_asnmb3t5yfk4024c6dbb6'
,attribute_name=>
'enabled'
,attribute_value=>
'NO'
);
PL/SQL
procedure
successfully completed.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select object_id%'
;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ----------------------------------------------------------------------
SYS_SQL_ac526b1e4be74880 SQL_PLAN_asnmb3t5yfk4024c6dbb6 AUTO-CAPTURE
NO
YES
select
object_id,object_name
from
t2
where
object_id
between
103
and
108
SYS_SQL_ac526b1e4be74880 SQL_PLAN_asnmb3t5yfk40b860bcf2 AUTO-CAPTURE YES YES
select
object_id,object_name
from
t2
where
object_id
between
103
and
108
再次执行目标 SQL
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.
从上述显示可以看出,现在 SQL 的执行计划已经变为了全表扫描,我们要启用新的执行计划 (全表扫描) 的目的已经实现,Note 部分也有了提示。
从上述测试结果可以看出,实际上我们可以轻易地在目标 SQL 的多个执行计划中切换,所以 SPM 确实是既能够主动地稳定执行计划,又保留了继续使用新的执行计划的机会,并且我们很容易就能启用新的执行计划。
下面介绍手工生成 SQL Plan Baseline:
手工生成目标 SQL 的 SQL Plan Baseline 其实非常简单,其核心就是调用 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE。这里只讨论针对单个 SQL 的 SQL Plan Baseline 的手工生成。
之前介绍过用 Manual 类型的 SQL Profile 可以在不改变目标 SQL 的 SQL 文本的情况下调整其执行计划。实际上,用手工生成 SQL Plan Baseline 的方式也完全可以实现同样的目的,甚至会比使用 Manual 类型的 SQL Profile 更加简洁。
手工生成目标 SQL 的 SQL Plan Baseline 的具体步骤为:
1)针对目标 SQL 使用 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE 手工生成其初始执行计划所对应的 SQL Plan Baseline。此时,使用 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE 传入的参数如下所示:
dbms_spm.load_plans_from_cursor_cache(sql_id=>’ 原目标 SQL 的 SQL_ID’,plan_hash_value=> 原目标 SQL 的 PLAN HASH VALUE)
2)改写原目标 SQL 的 SQL 文本,在其中加入合适的 Hint,直到加入 Hint 后的所改写的 SQL 能走出我们想要的执行计划,然后对改写后的 SQL 使用 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE 手工生成新的执行计划所对应的 SQL Plan Baseline。此时传入的参数如下所示:
dbms_spm.load_plans_from_cursor_cache(sql_id=>’ 加入合适 Hint 后改写 SQL 的 SQL_ID’,plan_hash_value=> 加入合适 Hint 后改写 SQL 的 PLAN HASH VALUE,sql_handle=>’ 原目标 SQL 在步骤 (1) 中所产生的 SQL Plan Baseline 的 sql_handle’)
3)使用 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE 删除步骤 (1) 中手工生成的原目标 SQL 的初始执行计划所对应的 SQL Plan Baseline。此时传入的参数如下所示:
dbms_spm.drop_sql_plan_baseline(sql_handle=>’ 原目标 SQL 在步骤 (1) 中所产生的 SQL Plan Baseline 的 sql_handle’,plan_name=>’ 原目标 SQL 在步骤 (1) 中所产生的 SQL Plan Baseline 的 plan_name’)
下面使用一个实例演示:
zx@MYDB>
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4;
OBJECT_NAME OBJECT_ID
------------------------------ ----------
TAB$ 4
zx@MYDB>
select
*
from
table
(dbms_xplan.display_cursor(
null
,
null
,
'advanced'
));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0n5z3wmf8qpgn, child number 0
-------------------------------------
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4
Plan hash value: 1513984157
--------------------------------------------------------------------------
| Id | Operation |
Name
|
Rows
| Bytes | Cost (%CPU)|
Time
|
--------------------------------------------------------------------------
| 0 |
SELECT
STATEMENT | | | | 287 (100)| |
|* 1 |
TABLE
ACCESS
FULL
| T2 | 1 | 30 | 287 (1)| 00:00:04 |
--------------------------------------------------------------------------
Query Block
Name
/ Object Alias (identified
by
operation id):
-------------------------------------------------------------
1 - SEL$1 / T2@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE(
'11.2.0.1'
)
DB_VERSION(
'11.2.0.1'
)
ALL_ROWS
OUTLINE_LEAF(@
"SEL$1"
)
FULL
(@
"SEL$1"
"T2"
@
"SEL$1"
)
END_OUTLINE_DATA
*/
Predicate Information (identified
by
operation id):
---------------------------------------------------
1 - filter(
"OBJECT_ID"
=4)
Column
Projection Information (identified
by
operation id):
-----------------------------------------------------------
1 -
"OBJECT_NAME"
[VARCHAR2,128],
"OBJECT_ID"
[NUMBER,22]
43
rows
selected.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select /*+ no_index(t2 idx_t2)%'
;
no
rows
selected
zx@MYDB>var
temp
number
zx@MYDB>
exec
:
temp
:=dbms_spm.load_plans_from_cursor_cache(sql_id=>
'0n5z3wmf8qpgn'
,plan_hash_value=>1513984157);
PL/SQL
procedure
successfully completed.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select /*+ no_index(t2 idx_t2)%'
;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ----------------------------------------------------------------------
SYS_SQL_75b06ae056223f5f SQL_PLAN_7bc3aw1b24guzb860bcf2 MANUAL-
LOAD
YES YES
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_i
d=4
从上述显示目标 SQL 初始执行计划为全表扫描,sql_id 和 plan hash value 可以从执行计划中找到,由于没有启用自动捕获 SQL Plan Baseline,一开始没有查到目标 SQL 对应的 SQL Plan Baseline,手工生成后,可以查到全表扫描对应的 SQL Plan Baseline。
改写原目标 SQL,加入 Hint 后重新执行:
zx@MYDB>
select
/*+
index
(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4;
OBJECT_NAME OBJECT_ID
------------------------------ ----------
TAB$ 4
zx@MYDB>
select
*
from
table
(dbms_xplan.display_cursor(
null
,
null
,
'advanced'
));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 60txg87j30pvw, child number 0
-------------------------------------
select
/*+
index
(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4
Plan hash value: 2008370210
--------------------------------------------------------------------------------------
| Id | Operation |
Name
|
Rows
| Bytes | Cost (%CPU)|
Time
|
--------------------------------------------------------------------------------------
| 0 |
SELECT
STATEMENT | | | | 335 (100)| |
| 1 |
TABLE
ACCESS
BY
INDEX
ROWID| T2 | 1 | 30 | 335 (0)| 00:00:05 |
|* 2 |
INDEX
RANGE SCAN | IDX_T2 | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Query Block
Name
/ Object Alias (identified
by
operation id):
-------------------------------------------------------------
1 - SEL$1 / T2@SEL$1
2 - SEL$1 / T2@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE(
'11.2.0.1'
)
DB_VERSION(
'11.2.0.1'
)
ALL_ROWS
OUTLINE_LEAF(@
"SEL$1"
)
INDEX_RS_ASC(@
"SEL$1"
"T2"
@
"SEL$1"
(
"T2"
.
"OBJECT_ID"
))
END_OUTLINE_DATA
*/
Predicate Information (identified
by
operation id):
---------------------------------------------------
2 - access(
"OBJECT_ID"
=4)
Column
Projection Information (identified
by
operation id):
-----------------------------------------------------------
1 -
"OBJECT_NAME"
[VARCHAR2,128],
"OBJECT_ID"
[NUMBER,22]
2 -
"T2"
.ROWID[ROWID,10],
"OBJECT_ID"
[NUMBER,22]
46
rows
selected.
zx@MYDB>
exec
:
temp
:=dbms_spm.load_plans_from_cursor_cache(sql_id=>
'60txg87j30pvw'
,plan_hash_value=>2008370210,sql_handle=>
'SYS_SQL_75b06ae056223f5f'
);
PL/SQL
procedure
successfully completed.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select /*+ no_index(t2 idx_t2)%'
;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ----------------------------------------------------------------------
SYS_SQL_75b06ae056223f5f SQL_PLAN_7bc3aw1b24guz24c6dbb6 MANUAL-
LOAD
YES YES
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_i
d=4
SYS_SQL_75b06ae056223f5f SQL_PLAN_7bc3aw1b24guzb860bcf2 MANUAL-
LOAD
YES YES
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_i
d=4
从上述输出可以看出把改写过的 SQL 的新的执行计划所对应的 SQL Plan Baseline 已经成功生成,而且所有手工生成的 SQL Plan Baseline 的 ENABLED 和 ACCEPTED 的值均为 YES,这是和自动捕获的 SQL Plan Baseline 不一样的地方。
Drop 掉原执行计划 (全表扫描) 所对应的 SQL Plan Baseline:
zx@MYDB>
exec
:
temp
:= dbms_spm.drop_sql_plan_baseline(sql_handle=>
'SYS_SQL_75b06ae056223f5f'
,plan_name=>
'SQL_PLAN_7bc3aw1b24guzb860bcf2'
);
PL/SQL
procedure
successfully completed.
zx@MYDB>
select
sql_handle,plan_name,origin,enabled,accepted,sql_text
from
dba_sql_plan_baselines
where
sql_text
like
'select /*+ no_index(t2 idx_t2)%'
;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED SQL_TEXT
------------------------------ ------------------------------ -------------------- --------- --------- ----------------------------------------------------------------------
SYS_SQL_75b06ae056223f5f SQL_PLAN_7bc3aw1b24guz24c6dbb6 MANUAL-
LOAD
YES YES
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_i
d=4
再次执行原目标 SQL,并查看执行计划
zx@MYDB>
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4;
OBJECT_NAME OBJECT_ID
------------------------------ ----------
TAB$ 4
zx@MYDB>
select
*
from
table
(dbms_xplan.display_cursor(
null
,
null
,
'advanced'
));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0n5z3wmf8qpgn, child number 2
-------------------------------------
select
/*+ no_index(t2 idx_t2) */ object_name,object_id
from
t2
where
object_id=4
Plan hash value: 2008370210
--------------------------------------------------------------------------------------
| Id | Operation |
Name
|
Rows
| Bytes | Cost (%CPU)|
Time
|
--------------------------------------------------------------------------------------
| 0 |
SELECT
STATEMENT | | | | 335 (100)| |
| 1 |
TABLE
ACCESS
BY
INDEX
ROWID| T2 | 1 | 30 | 335 (0)| 00:00:05 |
|* 2 |
INDEX
RANGE SCAN | IDX_T2 | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Query Block
Name
/ Object Alias (identified
by
operation id):
-------------------------------------------------------------
1 - SEL$1 / T2@SEL$1
2 - SEL$1 / T2@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE(
'11.2.0.1'
)
DB_VERSION(
'11.2.0.1'
)
ALL_ROWS
OUTLINE_LEAF(@
"SEL$1"
)
INDEX_RS_ASC(@
"SEL$1"
"T2"
@
"SEL$1"
(
"T2"
.
"OBJECT_ID"
))
END_OUTLINE_DATA
*/
Predicate Information (identified
by
operation id):
---------------------------------------------------
2 - access(
"OBJECT_ID"
=4)
Column
Projection Information (identified
by
operation id):
-----------------------------------------------------------
1 -
"OBJECT_NAME"
[VARCHAR2,128],
"OBJECT_ID"
[NUMBER,22]
2 -
"T2"
.ROWID[ROWID,10],
"OBJECT_ID"
[NUMBER,22]
Note
-----
- SQL plan baseline SQL_PLAN_7bc3aw1b24guz24c6dbb6 used
for
this statement
50
rows
selected.
从上述输出可以看出,原目标 SQL 已经走了新的执行计划(索引范围扫描),而且 Note 部分也有提示“SQL plan baseline SQL_PLAN_7bc3aw1b24guz24c6dbb6 used for this statement”说明走了 SPM。