小豹子的网络记事本

记录每一个有意思的细节

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

一、前言

前面文章(https://www.cnblogs.com/ddzj01/p/11365541.html)给大家介绍了当一条sql有多个执行计划时,如何通过spm去绑定其中一条执行计划。本文将继续介绍,如何给一条sql注入一个新的执行计划,去替换原始的执行计划。


二、解决办法

1. 生成初始执行计划所对应的sql plan baseline

begin
:temp := dbms_spm.load_plans_from_cursor_cache(
sql_id => '原目标sql的sql_id',
plan_hash_value => 原目标sql的plan hash value);
end;
/

 

2. 查出该sql的sql_handle

select sql_handle, sql_text, plan_name, origin from dba_sql_plan_baselines where sql_text like '原目标sql的sql_text%';

 

3. 生成新的sql plan baseline

begin
:temp := 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_handle');
end;
/

 

4. 删除步骤(1)中所产生的sql plan baseline

begin
:temp := dbms_spm.drop_sql_plan_baseline(
sql_handle => '原目标sql在步骤(1)中的sql_handle',
plan_name => '原目标sql在步骤(1)中的plan_name');
end;
/

就这么写,肯定一脸懵逼,下面通过一个实验去解释。


三、做个实验

实验环境,使用scott账号,并给scott赋予dba权限(实际上scott只需要administer sql management object权限就可以使用spm)

创建表和索引,并收集统计信息

SQL> create table test2 as select * from dba_objects;
SQL> create index idx_test2 on test2(object_id) online;

SQL> begin
dbms_stats.gather_table_stats(ownname=>'SCOTT',
tabname=>'TEST2',
cascade=>true,
no_invalidate=>false);
end;
/

 

执行原始的sql

SQL> set autot trace
SQL> select * from test2 where object_id=20;

复制代码
Execution Plan
----------------------------------------------------------
Plan hash value: 4047680367

-----------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST2     |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST2 |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
复制代码

可以看到原始sql的执行计划为索引范围扫描

 

新开一个窗口,查原始sql的sql_id和plan_hash_value

SQL> col sql_id for a20
SQL> col sql_text for a40
SQL> select sql_id, plan_hash_value, sql_text from v$sqlarea where sql_text like 'select * from test2 where object_id=20%';

SQL_ID               PLAN_HASH_VALUE SQL_TEXT
-------------------- --------------- ----------------------------------------
4tm6j886yvzj3             4047680367 select * from test2 where object_id=20

 

将原始sql语句加入基线

SQL> var temp number;
SQL> begin 
:temp := dbms_spm.load_plans_from_cursor_cache(sql_id=>'4tm6j886yvzj3', plan_hash_value=>4047680367);
end;
/

 

查看原始sql的基线

SQL> col sql_handle for a24
SQL> col sql_text for a40
SQL> col plan_name for a35
SQL> select sql_handle, sql_text, plan_name, origin from dba_sql_plan_baselines where sql_text like 'select * from test2 where object_id=20%';

SQL_HANDLE               SQL_TEXT                                 PLAN_NAME                           ORIGIN
------------------------ ---------------------------------------- ----------------------------------- --------------
SQL_20df29fdb3e8ac52     select * from test2 where object_id=20   SQL_PLAN_21rt9zqtyjb2k60b1ef84      MANUAL-LOAD

 

对原始sql加hint,执行新的sql

SQL> select /*+ full(test2) */ * from test2 where object_id=20;

 

获得新sql语句的sql_id和plan_hash_value

SQL> select sql_id, plan_hash_value, sql_text from v$sqlarea where sql_text like 'select /*+ full(test2) */%';

SQL_ID               PLAN_HASH_VALUE SQL_TEXT
-------------------- --------------- ----------------------------------------
11cptg7m2vcwr              300966803 select /*+ full(test2) */ * from test2 w
                                     here object_id=20

 

将新的sql_id和plan_hash_value加入到原始sql的基线中

SQL> var temp number;
SQL> begin
:temp := dbms_spm.load_plans_from_cursor_cache(sql_id=>'11cptg7m2vcwr', plan_hash_value=>300966803, sql_handle =>'SQL_20df29fdb3e8ac52');
end;
/

 

查看原始sql的基线

SQL> select sql_handle, sql_text, plan_name, origin from dba_sql_plan_baselines where sql_text like 'select * from test2 where object_id=20%';

SQL_HANDLE               SQL_TEXT                                 PLAN_NAME                           ORIGIN
------------------------ ---------------------------------------- ----------------------------------- --------------
SQL_20df29fdb3e8ac52     select * from test2 where object_id=20   SQL_PLAN_21rt9zqtyjb2k60b1ef84      MANUAL-LOAD
SQL_20df29fdb3e8ac52     select * from test2 where object_id=20   SQL_PLAN_21rt9zqtyjb2k99963deb      MANUAL-LOAD

可以看到新的执行计划plan_name为SQL_PLAN_21rt9zqtyjb2k99963deb已经加到原始sql的基线中了

 

删除旧的sql_plan

SQL> var temp number;
SQL> begin
:temp := dbms_spm.drop_sql_plan_baseline(sql_handle =>'SQL_20df29fdb3e8ac52', plan_name=>'SQL_PLAN_21rt9zqtyjb2k60b1ef84');
end;
/

 

新开一个窗口,再运行原始sql语句

SQL> set autot trace
SQL> select * from test2 where object_id=20;

复制代码
Execution Plan
----------------------------------------------------------
Plan hash value: 300966803

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |     1 |    98 |   347   (1)| 00:00:05 |
|*  1 |  TABLE ACCESS FULL| TEST2 |     1 |    98 |   347   (1)| 00:00:05 |
---------------------------------------------------------------------------

Note
-----
   - SQL plan baseline "SQL_PLAN_21rt9zqtyjb2k99963deb" used for this statement
复制代码

可以看到原始sql的执行计划已经改变了。


四、总结

spm只是一个临时应急解决方案,数据库出现执行计划不正确,应该从源头查找原因,比如说统计信息,索引是否失效等等。

posted @   小豹子加油  阅读(1409)  评论(1编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
· 全程使用 AI 从 0 到 1 写了个小工具
点击右上角即可分享
微信分享提示