Oracle - SPM固定执行计划(一)
一、前言
生产中偶尔会碰到一些sql,有多种执行计划,其中部分情况是统计信息过旧造成的,重新收集下统计信息就行了。但是有些时候重新收集统计信息也解决不了问题,而开发又在嗷嗷叫,没时间让你去慢慢分析原因的时候,这时临时的解决办法是通过spm去固定一个正确的执行计划,等找到真正原因后再解除该spm。
二、解决办法
1. 通过dbms_xplan.display_cursor查看指定sql都有哪些执行计划
SQL> select * from table(dbms_xplan.display_cursor('&sql_id',null,'TYPICAL PEEKED_BINDS'));
Enter value for sql_id: 66a4184u0t6hn
old 1: select * from table(dbms_xplan.display_cursor('&sql_id',null,'TYPICAL PEEKED_BINDS'))
new 1: select * from table(dbms_xplan.display_cursor('66a4184u0t6hn',null,'TYPICAL PEEKED_BINDS')) SQL_ID 66a4184u0t6hn, child number 0
-------------------------------------
select /*for_test*/ * from test1 where object_id = 1 Plan hash value: 4122059633 ---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 693 (100)| |
|* 1 | TABLE ACCESS FULL| TEST1 | 173K| 15M| 693 (1)| 00:00:09 |
--------------------------------------------------------------------------- SQL_ID 66a4184u0t6hn, child number 1
-------------------------------------
select /*for_test*/ * from test1 where object_id = 1 Plan hash value: 2214001748 -----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 11 | 1056 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST1 | | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
2. 查询该sql的历史执行情况
SQL> col snap_id for 99999999
SQL> col date_time for a30
SQL> col plan_hash for 9999999999
SQL> col executions for 99999999
SQL> col avg_etime_s heading 'etime/exec' for 9999999.99
SQL> col avg_lio heading 'buffer/exec' for 99999999999
SQL> col avg_pio heading 'diskread/exec' for 99999999999
SQL> col avg_cputime_s heading 'cputim/exec' for 9999999.99
SQL> col avg_row heading 'rows/exec' for 9999999
SQL> select * from(
select distinct
s.snap_id,
to_char(s.begin_interval_time,'mm/dd/yy_hh24mi') || to_char(s.end_interval_time,'_hh24mi') date_time,
sql.plan_hash_value plan_hash,
sql.executions_delta executions,
(sql.elapsed_time_delta/1000000)/decode(sql.executions_delta,null,1,0,1,sql.executions_delta) avg_etime_s,
sql.buffer_gets_delta/decode(sql.executions_delta,null,1,0,1,sql.executions_delta) avg_lio,
sql.disk_reads_delta/decode(sql.executions_delta,null,1,0,1,sql.executions_delta) avg_pio,
(sql.cpu_time_delta/1000000)/decode(sql.executions_delta,null,1,0,1,sql.executions_delta) avg_cputime_s,
sql.rows_processed_total/decode(sql.executions_delta,null,1,0,1,sql.executions_delta) avg_row
from dba_hist_sqlstat sql, dba_hist_snapshot s
where sql.instance_number =(select instance_number from v$instance)
and sql.dbid =(select dbid from v$database)
and s.snap_id = sql.snap_id
and sql_id = trim('&sql_id') order by s.snap_id desc)
where rownum <= 100;
Enter value for sql_id: 66a4184u0t6hn
old 16: and sql_id = trim('&sql_id') order by s.snap_id desc)
new 16: and sql_id = trim('66a4184u0t6hn') order by s.snap_id desc) SNAP_ID DATE_TIME PLAN_HASH EXECUTIONS etime/exec buffer/exec diskread/exec cputim/exec rows/exec
--------- ------------------------------ ----------- ---------- ----------- ------------ ------------- ----------- ---------
39 08/16/19_1500_1600 2214001748 1 .12 25839 2901 .10 173927
39 08/16/19_1500_1600 4122059633 3 .11 13992 847 .11 173927
3. 绑定执行计划
从前两步中可以看到该sql有两条执行计划,假如plan_hash_value为’2214001748’才是对的,而此时数据库选择的是另一条执行计划,我们可以通过执行以下function去将执行计划固定为我们想要的。
SQL> var temp number;
SQL> begin
:temp := dbms_spm.load_plans_from_cursor_cache(sql_id=>'66a4184u0t6hn', plan_hash_value=>2214001748);
end;
/
三、做个实验
1. 准备测试表
实验环境,使用scott账号,并给scott赋予dba权限
SQL> create table test1 as select * from dba_objects;
SQL> insert into test1 select * from test1;
SQL> update test1 set object_id = 1 where rownum < (select count(*) from test1) - 10;
SQL> commit;
SQL> select object_id, count(*) from test1 group by object_id;
OBJECT_ID COUNT(*)
---------- ----------
1 173927
82112 1
82121 1
82118 1
82119 1
82122 1
82113 1
82114 1
82120 1
82115 1
82116 1
82117 1
2. 创建索引并收集统计信息
SQL> create index idx_test1 on test1(object_id) online;
SQL> begin
dbms_stats.gather_table_stats(ownname => 'SCOTT',
tabname => 'TEST1',
cascade => true,
method_opt => 'for columns object_id size 10',
no_invalidate => false);
end;
/
3. 通过修改优化器模式,模拟同样的sql产生两条不同的执行计划
开启一个窗口A
SQL> set autot trace
SQL> alter session set optimizer_mode = all_rows; // 11g默认的值
SQL> select /*for_test*/ * from test1 where object_id = 1;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633 ---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 173K| 15M| 693 (1)| 00:00:09 |
|* 1 | TABLE ACCESS FULL| TEST1 | 173K| 15M| 693 (1)| 00:00:09 |
---------------------------------------------------------------------------
开启另一个窗口B
SQL> set autot trace
SQL> alter session set optimizer_mode = first_rows_10;
SQL> select /*for_test*/ * from test1 where object_id = 1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2214001748 -----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 1056 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 11 | 1056 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST1 | | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
再开启一个窗口C
SQL> select sql_id, sql_text, optimizer_mode, plan_hash_value, child_number from v$sql where sql_text like 'select /*for_test*/ * from test1%';
SQL_ID SQL_TEXT OPTIMIZER_ PLAN_HASH_VALUE CHILD_NUMBER
------------- ------------------------------------------------------- ---------- --------------- ------------
66a4184u0t6hn select /*for_test*/ * from test1 where object_id = 1 ALL_ROWS 4122059633 0
66a4184u0t6hn select /*for_test*/ * from test1 where object_id = 1 FIRST_ROWS 2214001748 1
可以看到,因为优化器模式的不同,相同的sql产生了两条截然不同的执行计划
当optimizer_mode = all_rows为全表扫描,当optimizer_mode = first_rows_10为索引扫描
4. 绑定执行计划
再新开一个窗口D,执行
SQL> set autot trace
SQL> select /*for_test*/ * from test1 where object_id = 1;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633 ---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 173K| 15M| 693 (1)| 00:00:09 |
|* 1 | TABLE ACCESS FULL| TEST1 | 173K| 15M| 693 (1)| 00:00:09 |
---------------------------------------------------------------------------
可以看到执行计划为全表扫描,跟窗口A一样,这个是正常的
通过执行以下function去将执行计划固定为索引扫描
SQL> var temp number;
SQL> begin
:temp := dbms_spm.load_plans_from_cursor_cache(sql_id=>'66a4184u0t6hn', plan_hash_value=>2214001748);
end;
/
再执行以下sql
SQL> select /*for_test*/ * from test1 where object_id = 1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2214001748 -----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 1056 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 11 | 1056 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST1 | 173K| | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------- Note
-----
- SQL plan baseline "SQL_PLAN_9657urkb9u2tnf24a05ff" used for this statement
可以看到spm已经生效了
四、删除spm
当我们找到sql执行计划突变的原因了,解决问题之后,就可以删除spm了。如何删除spm呢?
新开窗口E
查看当前sql的执行计划基线
SQL> select sql_handle, plan_name, origin from dba_sql_plan_baselines where sql_text like 'select /*for_test*/ * from test1%';
SQL_HANDLE PLAN_NAME ORIGIN
------------------------------ ------------------------------ --------------
SQL_9314fabc969d0b34 SQL_PLAN_9657urkb9u2tnf24a05ff MANUAL-LOAD
SQL_9314fabc969d0b34 SQL_PLAN_9657urkb9u2tnfe026eff AUTO-CAPTURE
可以看到该sql有两条PLAN_NAME,一个是系统自动捕获的,一个是我们手工绑定的,反正我们不再需要这个了,统统删除
通过执行以下function去将执行计划基线删除
SQL> var temp number;
SQL> begin
:temp := dbms_spm.drop_sql_plan_baseline(sql_handle=>'SQL_9314fabc969d0b34', plan_name=>NULL);
end;
/
查看当前sql的执行计划基线
SQL> select sql_handle, plan_name, origin from dba_sql_plan_baselines where sql_text like 'select /*for_test*/ * from test1%';
no rows selected
再在窗口D中执行以下sql
SQL> select /*for_test*/ * from test1 where object_id = 1;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633 ---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 173K| 15M| 693 (1)| 00:00:09 |
|* 1 | TABLE ACCESS FULL| TEST1 | 173K| 15M| 693 (1)| 00:00:09 |
---------------------------------------------------------------------------
可以看到执行计划又变成默认的全表扫描了
五、说明
文章例子整理于《基于oracle的sql优化》,后面将写另一个场景,就是如果系统里就一个执行计划,但是该执行计划是有问题的,如何去手工生成一个正确的执行计划,然后绑定。
Oracle - SPM固定执行计划(一)的更多相关文章
- Oracle - SPM固定执行计划
1. 通过dbms_xplan.display_cursor查看指定sql都有哪些执行计划 SQL> select * from table(dbms_xplan.display_cursor( ...
- Oracle - SPM固定执行计划(二)
一.前言 前面文章(https://www.cnblogs.com/ddzj01/p/11365541.html)给大家介绍了当一条sql有多个执行计划时,如何通过spm去绑定其中一条执行计划.本文将 ...
- SQL Server如何固定执行计划
SQL Server 其实从SQL Server 2005开始,也提供了类似ORACLE中固定执行计划的功能,只是好像很少人使用这个功能.当然在SQL Server中不叫"固定执行计划&qu ...
- Oracle 固定执行计划-使用SPM(Sql Plan Management)固定执行计划
固定执行计划-使用SPM(Sql Plan Management)固定执行计划 转载自:http://www.lunar2013.com/2016/01/固定执行计划-使用spm%EF%BC%88sq ...
- 基于Oracle的SQL优化(崔华著)-整理笔记-第2章“Oracle里的执行计划”
详细介绍了Oracle数据里与执行计划有关的各个方面的内容,包括执行计划的含义,加何查看执行计划,如何得到目标SQL真实的执行计划,如何查看执行计划的执行顺序,Oracle数据库里各种常见的执行计划的 ...
- 固定执行计划-SQL PROFILE手工绑定
固定(稳定)执行计划 你的应用的功能时快时慢,变化比较大,功能的性能能够保持一种稳定的状态,ORACLE 固定执行计划,采用以下这几种方式 oracle 9i使用 Outline oracle 10g ...
- Oracle中获取执行计划的几种方法分析
以下是对Oracle中获取执行计划的几种方法进行了详细的分析介绍,需要的朋友可以参考下 1. 预估执行计划 - Explain PlanExplain plan以SQL语句作为输入,得到这条S ...
- Oracle查看SQL执行计划的方式
Oracle查看SQL执行计划的方式 获取Oracle sql执行计划并查看执行计划,是掌握和判断数据库性能的基本技巧.下面案例介绍了多种查看sql执行计划的方式: 基本有以下几种方式: ...
- Oracle性能优化之Oracle里的执行计划
一.执行计划 执行计划是目标SQL在oracle数据库中具体的执行步骤,oracle用来执行目标SQL语句的具体执行步骤的组合被称为执行计划. 二.如何查看oracle数据库的执行计划 oracle数 ...
随机推荐
- 第三章.定制专属的kali
1.更新升级 • apt-get update • apt-get upgrade • apt-get dis-upgrade 2.根据个人喜好需求安装软件包 • 库 • Apt-get命令 • ...
- Windows OS添加USB3.0驱动!
原因:现在的win7.win2008等一些早期的系统在安装时无法使USB鼠标和键盘... 1. 素材(dism64工具.UltraISO工具.U盘一个) 2. 先使用UltrISO工具制作一个wi ...
- SSRS报表-级联筛选参数刷新后不能默认全选 -问题解决方案
好久没有写博客了,最近更新完善修复了SSRS报表的一些问题,和大家分享. 问题描述: 报表中,区域->专区->省份->地级市 此四个筛选参数是联动的,在DataSet中前一父级参数作 ...
- [原创]OpenvSwitch安装
一.安装环境: ubuntu-12.04-64bit 二.使用root权限,安装所需软件: apt-get install build-essential apt-get install openss ...
- 手写 Spring
手写 Spring 不多说,简历装 X 必备.不过练好还是需要求一定的思维能力. 一.整体思路 思路要熟练背下来 1)配置阶段 配置 web.xml: XDispatchServlet 设定 init ...
- Excel催化剂开源第34波-SM.MS图床API调用(用POST上传multipart/form-data内容)
日常做网抓数据,都是以GET请求为主,偶尔遇到需要POST请求的,一般POST的参数只是一串字符串就可以了,通过构造字符串也很容易完成,但此次SM.MS的API接口要求是Content-Type: m ...
- sql注入------基于时间延迟benchmark函数注入脚本
#author:windy_2import requests urlx = 'http://127.0.0.1/?id= 1 and if((substr((select database()),' ...
- Python字符串格式化-学这些就够用了
一.思考❓❔ 1.什么是字符串格式化? 将变量(对象)的值填充到字符串中 在字符串中解析Python表达式 对字符串进行格式化显示 左对齐.右对齐.居中对齐 保留数字有效位数 2.你学过的字符串格式化 ...
- eclipse(java windows)
百度云:链接:http://pan.baidu.com/s/1i4Zjv97 密码:u0qh 官方下载网址:http://www.eclipse.org/downloads/eclipse-pa ...
- linux初学者-squid代理篇
linux初学者-squid代理篇 Squid代理服务器是一种缓存服务器,一般分为正向代理和反向代理. 1.正向代理 客户端因为网络或者其他的问题,不能访问到一台Apache服务器,如果要访问到,则 ...