阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Oracle固定SQL的执行计划(二)—SPM

153次阅读
没有评论

共计 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
 
rows selected.

Oracle 固定 SQL 的执行计划(二)---SPM

从执行计划上看,走的是索引 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
 
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
 
rows selected.

Oracle 固定 SQL 的执行计划(二)---SPM

从执行计划中可以看出该 SQL 的执行计划已经变为全表扫描。因为目标 SQL 已经重复执行且同时又产生了一个新的执行计划,所以现在 Oracle 就会自动捕获并创建这个新的执行计划所对应的 SQL Plan Baseline 了。从如下查询可以看出 Oracle 对新的执行计划产生了一个新的 SQL Plan Baseline,其 ENABLED 的值依然为 YES,但 ACCEPTED 的值变为了 NO:

Oracle 固定 SQL 的执行计划 (二)---SPM 现在我们对当前 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
 
rows selected.

Oracle 固定 SQL 的执行计划(二)---SPM

Oracle 固定 SQL 的执行计划(二)---SPM

从上面的显示内容可以看出,现在目标 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.

Oracle 固定 SQL 的执行计划(二)---SPM

从上面显示的内容看到如下信息:“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
 
rows selected.

Oracle 固定 SQL 的执行计划(二)---SPM

Oracle 固定 SQL 的执行计划(二)---SPM

从上述显示可以看出,现在 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。

 

正文完
星哥说事-微信公众号
post-qrcode
 
星锅
版权声明:本站原创文章,由 星锅 2022-01-22发表,共计19820字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中