########sample 执行计划突然变化

问题:

接受到一条信息,执行计划突然变化了。

SELECT /*+ db120190621 no_expand */ INTERNAL_KEY FROM AA.RB_bb WHERE BASE_bb_NO = :B4 AND CCY = NVL (:B3 , CCY) AND bb_TYPE = NVL (:B2 , bb_TYPE) AND NVL (CERTIFICATE_NO, '~') = NVL (:B1 , '~')

快的:

"BASE_bb_NO"      : Field {  type=FieldType[string]  length=50  scale=0  value= {0017091458012} }

"CCY"       : Field {  type=FieldType[string]  length=3  scale=0  value= {dd} }

"bb_TYPE" : Field {  type=FieldType[string]  length=6  scale=0  value= {210008} }

慢的:

"BASE_bb_NO"      : Field {  type=FieldType[string]  length=50  scale=0  value= {512100008627000003} }

"CCY"       : Field {  type=FieldType[string]  length=3  scale=0  value= {dd} }

"bb_TYPE" : Field {  type=FieldType[string]  length=6  scale=0  value= {110002} }

CERTIFICATE_NO都是空值。

Inst: 1   Child: 0    Plan hash value: 2601219506

------------------------------------------------------------------------------------------

| Id  | Operation                     | Name    | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |

------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT              |         |        |       |     7 (100)|          |

|   1 |  CONCATENATION                |         |        |       |            |          |

|*  2 |   FILTER                      |         |        |       |            |          |

|*  3 |    TABLE ACCESS BY INDEX ROWID| RB_bb |      1 |    40 |     5   (0)| 00:00:01 |

|*  4 |     INDEX RANGE SCAN          | RAC_I5  |      1 |       |     3   (0)| 00:00:01 |

|*  5 |   FILTER                      |         |        |       |            |          |

|*  6 |    TABLE ACCESS BY INDEX ROWID| RB_bb |      1 |    40 |     2   (0)| 00:00:01 |

|*  7 |     INDEX RANGE SCAN          | RAC_I1  |      1 |       |     2   (0)| 00:00:01 |

------------------------------------------------------------------------------------------

现象:

->1.重现问题SQL,因为使使用绑定变量,所以使用绑定变量重现问题执行计划,同时使用hint 生成 硬解析 ##add hint /* */ 是为了重新硬解析 重要

另外,绑定变量值为空值的时候,也需要使用null值替代。这样在每次硬解析的时候,我们可以发现确实执行计划如应用描述一样,选择了2条不一样的执行计划。

绑定变量重现方法,如下:

sql1:RB_bb 97vurxcnctd16

var B1 varchar2(32);
var B2 VARCHAR2(32);
var B3 VARCHAR2(32);
var B4 VARCHAR2(32);
var B5 VARCHAR2(32);
exec :B1:=null;
exec :B2:='110002';
exec :B3:='dd';
exec :B4:='0000204742011';

SELECT /*+ db120190622 */ INTERNAL_KEY FROM AA.RB_bb WHERE BASE_bb_NO = :B4 AND CCY = :B3 AND bb_TYPE = NVL (:B2 , bb_TYPE) AND NVL (CERTIFICATE_NO, '~') =
NVL (:B1 , NVL(CERTIFICATE_NO, '~'))

select * from table(dbms_xplan.display_cursor());

-> 2.1同时查阅资料,发现这是条简单的单表查询,但在执行计划里使用了2次 index 范围查询。

第一次是

|*  4 |     INDEX RANGE SCAN          | RAC_I5  |      1 |       |     3   (0)| 00:00:01 |

第二次是

|*  7 |     INDEX RANGE SCAN          | RAC_I1  |      1 |       |     2   (0)| 00:00:01 |

检查原因,发现是11g 在执行绑定变量的函数查询时候,比如 NVL (:B2 , bb_TYPE) 查询,因为无法判断绑定变量是否是空值,所以会执行2次索引扫描查询,并将结果组合起来,返回结果,如下所示,当B2为null,执行一次索引扫描,B2 不为null,执行一次索引扫描,并将结果组合起来。

 Predicate Information (identified by operation id):
--------------------------------------------------- 2 - filter(:B2 IS NULL)
3 - filter((NVL("CERTIFICATE_NO",'~')=NVL(:B1,'~') AND "CCY"=NVL(:B3,"CCY")
AND "bb_TYPE" IS NOT NULL))
4 - access("BASE_bb_NO"=:B4)
5 - filter(:B2 IS NOT NULL)
6 - filter(("BASE_bb_NO"=:B4 AND NVL("CERTIFICATE_NO",'~')=NVL(:B1,'~') AND
"CCY"=NVL(:B3,"CCY")))
7 - access("bb_TYPE"=:B2)

->2.2 为了避免使用2次索引扫描,可以使用如下方法 no_expand  hint  来避免使用CONCATENATION  查询。

(https://www.cnblogs.com/wait4friend/archive/2012/12/14/2818164.html)

SELECT /*+ no_expand */

INTERNAL_KEY

FROM AA.RB_bb

WHERE     BASE_bb_NO = '516100292728100001'

AND CCY = NVL ('dd', CCY)

AND bb_TYPE = NVL ('110002', bb_TYPE)

AND NVL (CERTIFICATE_NO, '~') = NVL ('', '~')

执行计划应该如下:

Plan hash value: 1947998975

---------------------------------------------------------------------------------------

| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |         |     1 |    40 |     4   (0)| 00:00:01 |

|*  1 |  TABLE ACCESS BY INDEX ROWID| RB_bb |     1 |    40 |     4   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | RAC_I18 |     1 |       |     3   (0)| 00:00:01 |

---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter(NVL("CERTIFICATE_NO",'~')='~')

2 - access("BASE_bb_NO"='516100292728100001' AND "bb_TYPE"='110002' AND

"CCY"='dd')

3.该执行计划变化原因暂时也没查明:

理论上硬解析应该不会出现在数据库上,如果出现,有可能原因是数据库SQL可能运行在不同的节点上了。还有就是对象存在DDL操作。

Query Using Bind Variables Suddenly Starts to Perform Slowly ( Doc ID 387394.1 )

"When bind variables are used in a statement, it is assumed that cursor sharing is intended and that different invocations are supposed to use the same execution plan. If different invocations of the cursor would significantly benefit from different execution plans, then bind variables may have been used inappropriately in the SQL statement."

Additionally, bind peeking has been known to cause a different execution plan to be used on different nodes of a RAC cluster because each node has its own Shared Pool. Despite the same SQL, data, and statistics, the first time a cursor was hard parsed on each node a different set of bind values was presented to the optimizer, choosing a different plan on each node.

It is expected behavior that execution plan changes since the bind variables are different.
If want to use one specific execution plan, please fix the execution plan.

SQL> var nRet NUMBER
SQL> EXEC :nRet := dbms_spm.load_plans_from_cursor_cache(sql_id=>'<your sql_id> ',plan_hash_value=><your paln_id>,fixed=>'YES',enabled=>'YES');

可能原因:
总结来说,绑定变量窥探会于第一次硬解析的时候,“窥探“绑定变量的值,进而根据该值的信息,辅助选择更加准确的执行计划,就像上述示例中第一次执行A为条件的SQL,
知道A值占比重接近全表数据量,因此选择了全表扫描。但若绑定变量列分布不均匀,则绑定变量窥探的副作用会很明显,第二次以后的每次执行,无论绑定变量列值是什么,
都会仅使用第一次硬解析窥探的参数值,这就有可能选择错误的执行计划,就像上面这个实验中说明的,第二次使用B为条件的SQL,除非再次硬解析,否则这种情况不会改变。

规避方法:

1.1使用no_expand hint 的SQL 生成一个优化的执行计划,在替代掉生产有问题的SQL

->执行方法,强制sql 使用118 号索引

0.
删除之前的profile
EXEC DBMS_SQLTUNE.DROP_SQL_PROFILE(Name => 'coe_cdquh2m4d3gjm_4076902948');

1. 构造一个好的执行计划:

var B1 varchar2(32);
var B2 VARCHAR2(32);
var B3 VARCHAR2(32);
exec :B1:='120008';
exec :B2:='dd';
exec :B3:='0000204742011';

SELECT /*+ db120191110 no_expand */ INTERNAL_KEY FROM user.tab WHERE BASE_ACCT_NO = :B3 AND CCY = :B2 AND ACCT_TYPE = NVL (:B1 , ACCT_TYPE)

select * from table(dbms_xplan.display_cursor());

2.1、抓取该SQL信息,并捕获该SQL的执行信息文件

select sql_id,plan_hash_value,sql_text from v$sql where sql_text like '%/*+ db120191110 no_expand */%';

40bkjwtyvuzmu 1947998975

2.2、Run the script coe_xfr_sql_profile.sql as SYSDBA the sql_id and the good Plan Hash Value

START coe_xfr_sql_profile.sql <sql_id> <plan hash value for good plan>

@/db/db1/app/db/product/11204/rdbms/admin/coe_xfr_sql_profile.sql 40bkjwtyvuzmu 1947998975

2.3 修改该执行文件信息

->SQL_内容去掉 hint ,同时去掉 表名前的 用户名

wa(q'[SELECT /*+ test */TABLE_NAME, TABLE_OWNER, DB_LINK FROM ALL_SYNO]');

wa(q'[NYMS WHERE OWNER = 'PUBLIC' AND SYNONYM_NAME = 'test' ]');

==============================================================》

wa(q'[SELECT /*+RULE*/ TABLE_NAME, TABLE_OWNER, DB_LINK FROM ALL_SYNO]');

wa(q'[NYMS WHERE OWNER = 'PUBLIC' AND SYNONYM_NAME = 'test' ]');

->force_match 改成true
force_match => FALSE

====================》

force_match => TRUE

5、在数据库中执行绑定脚本

@coe_xfr_sql_profile_bpwkfmp2yzmdd_244914142.sql

5.2
select name,to_char(sql_text) from dba_sql_profiles
coe_faa81zfq2ws27_2769088497

6、测试语句是否使用此sqlprofile

--user user/userqaz
set autot on

var B1 varchar2(32);
var B2 VARCHAR2(32);
var B3 VARCHAR2(32);
exec :B1:='120008';
exec :B2:='dd';
exec :B3:='0000204742011';

SELECT INTERNAL_KEY FROM tabWHERE BASE_ACCT_NO = :B3 AND CCY = :B2 AND ACCT_TYPE = NVL (:B1 , ACCT_TYPE)

Note

-----

- SQL profile "coe_bpwkfmp2yzmdd_244914142" used for this statement-----说明绑定成功

2,开启数据库监控,监控执行计划变化的SQL,提前获取可能潜在的问题。

version 2: 最近一天的排在top 15位的 在最近3天有执行计划变化的SQL
select count(*),text
from (SELECT
CHR(10) || 'sql_id -> ' || sql_id text
FROM (SELECT /*+ NO_MERGE */
h.sql_id,
h.plan_hash_value,
MIN(s.end_interval_time) first_snap_time,
MAX(s.end_interval_time) last_snap_time
FROM dba_hist_sqlstat h, dba_hist_snapshot s
WHERE h.sql_id in
(select sql_id
from (SELECT h.dbid,
h.sql_id,
TO_CHAR(SUM(h.elapsed_time_total),
'999999999999999990D990') elapsed
FROM dba_hist_sqlstat h, dba_hist_snapshot s
WHERE h.executions_total > 0
AND s.snap_id = h.snap_id
AND s.dbid = h.dbid
AND s.instance_number = h.instance_number
AND CAST(s.end_interval_time AS DATE) >
SYSDATE - 1
GROUP BY h.dbid, h.sql_id
order by elapsed desc)
where ROWNUM <= 15)
AND h.snap_id = s.snap_id
AND h.dbid = s.dbid
and s.end_interval_time > sysdate - 3
AND h.instance_number = s.instance_number
GROUP BY h.plan_hash_value, h.sql_id
ORDER BY 2) v )
group by text
having count(*) > 1

##############1.

https://carlos-sierra.net/2014/11/02/finding-sql-with-performance-changing-over-time/

该sql 太复杂,执行时间太长,但是是很好参考资料。

Finding SQL with Performance changing over time

with 10 comments

I upgraded my database a couple of weeks ago and now my users complain their application is slower. They do not provide specifics but they “feel” it is running slower. Sounds familiar?

Every once in a while I get a request that goes like this: “how can I find if some SQL on my database is performing worse over time?”

It is very hard to deal with the ambiguities of some problems like “finding SQL that performs worse or better over time”. But if you simplify the problem and consider for example “Elapsed Time per Execution”, then you can easily produce a script like the one below, which returns a small list of SQL statements that seem to experience either a regression or an improvement over time. It uses linear regression on the ratio between “Elapsed Time per Execution” and its Median per SQL.

Then, If you are suspecting you have some SQL that may have regressed and need a hand to identify them, you can try this script below. It is now part of a small collection of scripts that you can download and use for free out of the cscripts link on the right hand side of this page, under “Downloads”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
----------------------------------------------------------------------------------------
--
-- File name:   sql_performance_changed.sql
--
-- Purpose:     Lists SQL Statements with Elapsed Time per Execution changing over time
--
-- Author:      Carlos Sierra
--
-- Version:     2014/10/31
--
-- Usage:       Lists statements that have changed their elapsed time per execution over
--              some history.
--              Uses the ration between "elapsed time per execution" and the median of
--              this metric for SQL statements within the sampled history, and using
--              linear regression identifies those that have changed the most. In other
--              words where the slope of the linear regression is larger. Positive slopes
--              are considered "improving" while negative are "regressing".
--
-- Example:     @sql_performance_changed.sql
--
-- Notes:       Developed and tested on 11.2.0.3.
--
--              Requires an Oracle Diagnostics Pack License since AWR data is accessed.
--
--              To further investigate poorly performing SQL use sqltxplain.sql or sqlhc
--              (or planx.sql or sqlmon.sql or sqlash.sql).
--            
---------------------------------------------------------------------------------------
--
SPO sql_performance_changed.txt;
DEF days_of_history_accessed = '31';
DEF captured_at_least_x_times = '10';
DEF captured_at_least_x_days_apart = '5';
DEF med_elap_microsecs_threshold = '1e4';
DEF min_slope_threshold = '0.1';
DEF max_num_rows = '20';
 
SET lin 200 ver OFF;
COL row_n FOR A2 HEA '#';
COL med_secs_per_exec HEA 'Median Secs|Per Exec';
COL std_secs_per_exec HEA 'Std Dev Secs|Per Exec';
COL avg_secs_per_exec HEA 'Avg Secs|Per Exec';
COL min_secs_per_exec HEA 'Min Secs|Per Exec';
COL max_secs_per_exec HEA 'Max Secs|Per Exec';
COL plans FOR 9999;
COL sql_text_80 FOR A80;
 
PRO SQL Statements with "Elapsed Time per Execution" changing over time
 
WITH
per_time AS (
SELECT h.dbid,
       h.sql_id,
       SYSDATE - CAST(s.end_interval_time AS DATE) days_ago,
       SUM(h.elapsed_time_total) / SUM(h.executions_total) time_per_exec
  FROM dba_hist_sqlstat h,
       dba_hist_snapshot s
 WHERE h.executions_total > 0
   AND s.snap_id = h.snap_id
   AND s.dbid = h.dbid
   AND s.instance_number = h.instance_number
   AND CAST(s.end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
 GROUP BY
       h.dbid,
       h.sql_id,
       SYSDATE - CAST(s.end_interval_time AS DATE)
),
avg_time AS (
SELECT dbid,
       sql_id,
       MEDIAN(time_per_exec) med_time_per_exec,
       STDDEV(time_per_exec) std_time_per_exec,
       AVG(time_per_exec)    avg_time_per_exec,
       MIN(time_per_exec)    min_time_per_exec,
       MAX(time_per_exec)    max_time_per_exec      
  FROM per_time
 GROUP BY
       dbid,
       sql_id
HAVING COUNT(*) >= &&captured_at_least_x_times.
   AND MAX(days_ago) - MIN(days_ago) >= &&captured_at_least_x_days_apart.
   AND MEDIAN(time_per_exec) > &&med_elap_microsecs_threshold.
),
time_over_median AS (
SELECT h.dbid,
       h.sql_id,
       h.days_ago,
       (h.time_per_exec / a.med_time_per_exec) time_per_exec_over_med,
       a.med_time_per_exec,
       a.std_time_per_exec,
       a.avg_time_per_exec,
       a.min_time_per_exec,
       a.max_time_per_exec
  FROM per_time h, avg_time a
 WHERE a.sql_id = h.sql_id
),
ranked AS (
SELECT RANK () OVER (ORDER BY ABS(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago)) DESC) rank_num,
       t.dbid,
       t.sql_id,
       CASE WHEN REGR_SLOPE(t.time_per_exec_over_med, t.days_ago) > 0 THEN 'IMPROVING' ELSE 'REGRESSING' END change,
       ROUND(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago), 3) slope,
       ROUND(AVG(t.med_time_per_exec)/1e6, 3) med_secs_per_exec,
       ROUND(AVG(t.std_time_per_exec)/1e6, 3) std_secs_per_exec,
       ROUND(AVG(t.avg_time_per_exec)/1e6, 3) avg_secs_per_exec,
       ROUND(MIN(t.min_time_per_exec)/1e6, 3) min_secs_per_exec,
       ROUND(MAX(t.max_time_per_exec)/1e6, 3) max_secs_per_exec
  FROM time_over_median t
 GROUP BY
       t.dbid,
       t.sql_id
HAVING ABS(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago)) > &&min_slope_threshold.
)
SELECT LPAD(ROWNUM, 2) row_n,
       r.sql_id,
       r.change,
       TO_CHAR(r.slope, '990.000MI') slope,
       TO_CHAR(r.med_secs_per_exec, '999,990.000') med_secs_per_exec,
       TO_CHAR(r.std_secs_per_exec, '999,990.000') std_secs_per_exec,
       TO_CHAR(r.avg_secs_per_exec, '999,990.000') avg_secs_per_exec,
       TO_CHAR(r.min_secs_per_exec, '999,990.000') min_secs_per_exec,
       TO_CHAR(r.max_secs_per_exec, '999,990.000') max_secs_per_exec,
       (SELECT COUNT(DISTINCT p.plan_hash_value) FROM dba_hist_sql_plan p WHERE p.dbid = r.dbid AND p.sql_id = r.sql_id) plans,
       REPLACE((SELECT DBMS_LOB.SUBSTR(s.sql_text, 80) FROM dba_hist_sqltext s WHERE s.dbid = r.dbid AND s.sql_id = r.sql_id), CHR(10)) sql_text_80
  FROM ranked r
 WHERE r.rank_num <= &&max_num_rows.
 ORDER BY
       r.rank_num
/
 
SPO OFF;

Once you get the output of this script above, you can use the one below to actually list the time series for one of the SQL statements of interest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
----------------------------------------------------------------------------------------
--
-- File name:   one_sql_time_series.sql
--
-- Purpose:     Performance History for one SQL
--
-- Author:      Carlos Sierra
--
-- Version:     2014/10/31
--
-- Usage:       Script sql_performance_changed.sql lists SQL Statements with performance
--              improvement or regressed over some History.
--              This script one_sql_time_series.sql lists the Performance Time Series for
--              one SQL.
--
-- Parameters:  SQL_ID
--
-- Example:     @one_sql_time_series.sql
--
-- Notes:       Developed and tested on 11.2.0.3.
--
--              Requires an Oracle Diagnostics Pack License since AWR data is accessed.
--
--              To further investigate poorly performing SQL use sqltxplain.sql or sqlhc
--              (or planx.sql or sqlmon.sql or sqlash.sql).
--            
---------------------------------------------------------------------------------------
--
SPO one_sql_time_series.txt;
SET lin 200 ver OFF;
 
COL instance_number FOR 9999 HEA 'Inst';
COL end_time HEA 'End Time';
COL plan_hash_value HEA 'Plan|Hash Value';
COL executions_total FOR 999,999 HEA 'Execs|Total';
COL rows_per_exec HEA 'Rows Per Exec';
COL et_secs_per_exec HEA 'Elap Secs|Per Exec';
COL cpu_secs_per_exec HEA 'CPU Secs|Per Exec';
COL io_secs_per_exec HEA 'IO Secs|Per Exec';
COL cl_secs_per_exec HEA 'Clus Secs|Per Exec';
COL ap_secs_per_exec HEA 'App Secs|Per Exec';
COL cc_secs_per_exec HEA 'Conc Secs|Per Exec';
COL pl_secs_per_exec HEA 'PLSQL Secs|Per Exec';
COL ja_secs_per_exec HEA 'Java Secs|Per Exec';
 
SELECT h.instance_number,
       TO_CHAR(CAST(s.end_interval_time AS DATE), 'YYYY-MM-DD HH24:MI') end_time,
       h.plan_hash_value,
       h.executions_total,
       TO_CHAR(ROUND(h.rows_processed_total / h.executions_total), '999,999,999,999') rows_per_exec,
       TO_CHAR(ROUND(h.elapsed_time_total / h.executions_total / 1e6, 3), '999,990.000') et_secs_per_exec,
       TO_CHAR(ROUND(h.cpu_time_total / h.executions_total / 1e6, 3), '999,990.000') cpu_secs_per_exec,
       TO_CHAR(ROUND(h.iowait_total / h.executions_total / 1e6, 3), '999,990.000') io_secs_per_exec,
       TO_CHAR(ROUND(h.clwait_total / h.executions_total / 1e6, 3), '999,990.000') cl_secs_per_exec,
       TO_CHAR(ROUND(h.apwait_total / h.executions_total / 1e6, 3), '999,990.000') ap_secs_per_exec,
       TO_CHAR(ROUND(h.ccwait_total / h.executions_total / 1e6, 3), '999,990.000') cc_secs_per_exec,
       TO_CHAR(ROUND(h.plsexec_time_total / h.executions_total / 1e6, 3), '999,990.000') pl_secs_per_exec,
       TO_CHAR(ROUND(h.javexec_time_total / h.executions_total / 1e6, 3), '999,990.000') ja_secs_per_exec
  FROM dba_hist_sqlstat h,
       dba_hist_snapshot s
 WHERE h.sql_id = '&sql_id.'
   AND h.executions_total > 0
   AND s.snap_id = h.snap_id
   AND s.dbid = h.dbid
   AND s.instance_number = h.instance_number
 ORDER BY
       h.sql_id,
       h.instance_number,
       s.end_interval_time,
       h.plan_hash_value
/
 
SPO OFF;
 
 
########2
 
https://www.cnblogs.com/pangblog/p/3268586.html
1、问题

       通过调用dbms_xplan包中DISPLAY_AWR函数(DBMS_XPLAN.DISPLAY_AWR)可以从AWR数据中查看到SQL语句的历史执行计划,但是,DISPLAY_AWR函数的可传入参数只有四种,分别为:sql_id、plan_hash_value、db_id、format,缺少与时间范围相关的参数、也没有instance_number相关参数。
       使用dbms_xplan.display_awr的简单方式,一般为:
         SQL>select * from table(dbms_xplan.display_awr(db_id=> 19948XXXX2,sql_id=> 'bj75p9188y410'));
 
        假如一套RAC环境,在8月5日的9:00—09:30时,2节点发生了CPU消耗非常高的情况,如果要分析是不是因为SQL_ID为bj75p9188y410 的语句的执行计划走错所致,这时,如果想用dbms_xplan.display_awr的简单查询方式来得到当时的执行计划,是无法实现的,那应该怎样查出该语句8月5日的9:00—09:30时第2节点上SQL_ID为bj75p9188y410的语句的执行计划是怎样子的呢?
2、分析
        如果通过DBMS_XPLAN.DISPLAY_AWR查看SQL语句的执行计划,将是从整个AWR数据库中查找,例如从AWR报告中查询SQL_ID为bj75p9188y410 的执行计划:
          SQL>select * from table(dbms_xplan.display_awr(db_id=> 19948XXXX2,sql_id=> 'bj75p9188y410'))
        为了以简短的篇幅展示出从AWR中总共查到了几种执行计划,我将语句改写如下:
          SQL> select * from table(dbms_xplan.display_awr(db_id=> 19948XXXX2,sql_id=> 'bj75p9188y410')) where      
               plan_table_output  like ('Planhash value%');
  结果如下:

1

Plan hash value: 6178145
2
Plan hash value: 2354817963
3
Plan hash value: 3990363694
        从此结果中看出,SQL_ID为bj75p9188y410 的语句在当前保留的AWR数据中存在三种执行计划。其中Plan hash value为3990363694的执行计划为错误的执行计划
实际生产环境中,在8月5日的9:00—09:30时,2节点发生了CPU消耗非常高的情况。现在就是要确认在此时间,该SQL_ID为bj75p9188y410的语句到底是使用哪个执行计划呢?
3、解决方法
3.1 、查到8月5日9:00—09:30的 snap_id
SQL>select dbid,snap_id,instance_number,begin_interval_time,end_interval_time
     fromdba_hist_snapshot
    wherebegin_interval_time >=to_date('2013-08-0509:00:00', 'yyyy-mm-dd hh24:mi:ss')
      andend_interval_time <=to_date('2013-08-0509:31:00', 'yyyy-mm-dd hh24:mi:ss')
结果为:

dbid

snap_id
instance_number
begin_interval_time
end_interval_time
19948XXXX2
33676
1
05-8月 -13 09.00.09.903
05-8月 -13 09.30.10.113
19948XXXX2
33676
2
05-8月 -13 09.00.09.786
05-8月 -13 09.30.10.502
3.2 、通过 dbms_xplan.display_awr   与包含snap_id、instance_number信息的视图关联得到8月5日9:00—09:30时SQL_ID为 bj75p9188y410   的执行计划:
  SQL>select a.* from (select distinct dbid,sql_id, plan_hash_value from dba_hist_sqlstat
          wheresql_id = 'bj75p9188y410'
            andsnap_id =   33676
            andinstance_number = 2) b,          table ( dbms_xplan.display_awr ( db_id =>   19948XXXX2 , sql_id =>  b.sql_id , plan_hash_value => b.plan_hash_value ))  a;
结果如下:
SQL_ID bj75p9188y410
--------------------
select * from ( select distinct b.XXXX_id as
……   (为了信息脱敏,真实语句在此省略)
,'NLS_SORT=SCHINESE_XXXX'),b.XXXX_name  ) where rownum <= :1
 
Plan hash value: 3990363694
---------------------------------------------------------------------------------------------------------------------
| Id  |  Operation                                              | Name                                    | Rows     | Bytes | Cost (%CPU)| Time       |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT  STATEMENT                             |                                             |            |           |      315 (100)|                 |
|   1 |  COUNT STOPKEY                                  |                                             |            |           |                     |                 |
|   2 |   VIEW                                                   |                                                |         1 |    180 |        315   (2)| 00:00:29 |
|   3 |    SORT ORDER BY STOPKEY                 |                                           |         1 |    151 |        315   (2)| 00:00:29 |
|   4 |     HASH UNIQUE                                    |                                              |         1 |    151 |        314   (1)| 00:00:29 |
|   5 |      FILTER                                              |                                              |            |           |                      |                |
|   6 |       NESTED LOOPS OUTER                   |                                           |         1 |    151 |         313   (1)| 00:00:29 |
|   7 |        NESTED LOOPS                              |                                            |         1 |      86 |           35   (0)| 00:00:04 |
|   8 |         TABLE ACCESS BY INDEX ROWID | LB_T_XXXX_PROVIDER            |        1 |      61 |           34    (0)| 00:00:04 |
|   9 |          INDEX RANGE SCAN                     | IDX_LB_T_XXXX_PROVIDER_003 | 183 |         |             3    (0)| 00:00:01 |
|  10 |         TABLE ACCESS BY INDEX ROWID | LA_XXXX                                 |         1 |      25 |            1    (0)| 00:00:01 |
|  11 |          INDEX UNIQUE SCAN                   | PK_LA_XXXX                           |         1 |           |            0    (0)|                 |
|  12 |        VIEW PUSHED PREDICATE            | LB_T_XXXX                             |         1 |      65 |         278   (1)| 00:00:26 |
|  13 |         MERGE JOIN OUTER                     |                                                |         1 |      64 |         278   (1)| 00:00:26 |
|  14 |          TABLE ACCESS BY INDEX ROWID| XXXX_SUPPLIER                     |         1 |      45 |        146    (0)| 00:00:14 |
|  15 |           INDEX FULL SCAN                       | PK_XXX_SUPPLIER                 |         1 |           |        145    (0)| 00:00:14 |
|  16 |          SORT JOIN                                  |                                                | 17998 |   333K|         132   (2)| 00:00:12 |
|  17 |           VIEW                                          |                                               | 17998 |   333K|         131   (1)| 00:00:12 |
|  18 |            SORT GROUP BY                       |                                                | 17998 |   544K|         131   (1)| 00:00:12 |
|  19 |             TABLE ACCESS FULL                | XXXX_SUPPLIER_CONTACT     | 30058 |   909K|         130   (0)| 00:00:12 |
---------------------------------------------------------------------------------------------------------------------
    此执行计划发生了严重的估算错误

###########3

没啥帮助

http://www.oracle-wiki.net/startscriptsplanmonitor

A Script to Monitor Plan Changes
Oracle Database » Script Library » A Script to Monitor Plan Changes

Description

The following script can be used monitor and alert on plan changes. Details of its use can be found in the headers of the script.

Plan_Change_Alert.ksh

#!/bin/ksh -x
############################################################################
#
# Author : Mark Ramsay
#
# History Date Name Reason
# ---- ---- ------
# 18 May 2011 Mark Ramsay Version 1.
#
# Description
#
# This script generates a report that shows if a SQL Plan has changed
# for a given SQL ID. It is useful for tracking plans for stubborn pieces
# of SQL that may have a few good plans and the occasional bad plan.
#
# The range of dates can be changed by setting SDS_range. However,
# this script would normally be scheduled each day the range will therefore
# be 1. i.e. Changes in the last 24hrs
#
# The user should set the variable SDS_sqlid to the SQLID that is being
# monitored. The variable SDS_hash_values should be set to the
# plan_hash_values that are acceptable for the given SQLID.
#
# If a new plan_hash_value is generated for the given SQLID, then
# the script will highlight this in the report.
#
# The report can then be mailed out to individuals to look into the plan
# change.
#
############################################################################
#
# Define Variables
# export ORACLE_SID=MYSID
export ORACLE_HOME=$(grep ^$ORACLE_SID: /var/opt/oracle/oratab |awk -F\: '{print $2}')
export ORACLE_BASE=/u01/app/oracle
export PATH=.:/usr/local/bin:/bin:/usr/sbin:/usr/bin:$ORACLE_HOME/bin SDS_date=`/bin/date '+%e_%B_%Y'|sed -e 's/ //'`
SDS_sqlid="'SQLID1','SQLID2'"
SDS_hash_values="HASH1,HASH2"
SDS_mail_addr=myemail@mydomain.com
SDS_range=1 SDS_output=`$ORACLE_HOME/bin/sqlplus -s '/ as sysdba' <<EOF set pagesize 0
set feedback off
set linesize 128
set heading off
set echo off SELECT distinct PLAN_HASH_VALUE
FROM dba_hist_sqlstat q,
(
SELECT /*+ NO_MERGE */ MIN(snap_id) min_snap, MAX(snap_id) max_snap
FROM dba_hist_snapshot ss
WHERE ss.begin_interval_time BETWEEN (SYSDATE - $SDS_range) AND SYSDATE
) s
WHERE q.snap_id BETWEEN s.min_snap AND s.max_snap
AND q.sql_id IN ( $SDS_sqlid)
AND q.plan_hash_value not in
($SDS_hash_values)
/ exit;
EOF` if [ -z "$SDS_output" ];
then
echo "All,
Explain Plan Change for SQLIDs: $SDS_sqlid - No Regards
" | mailx -s "Explain Plan Alert Report $SDS_date" $SDS_mail_addr
else
echo "All,
Explain Plan Change for SQLIDs: $SDS_sqlid - Yes
DBA to investigate.
Plan Hash Values: $SDS_output Regards
" | mailx -s "Explain Plan Alert Report $SDS_date" $SDS_mail_addr
fi exit 0

转 oracle 监控执行计划突然变化的更多相关文章

  1. Oracle sql执行计划解析

    Oracle sql执行计划解析 https://blog.csdn.net/xybelieve1990/article/details/50562963 Oracle优化器 Oracle的优化器共有 ...

  2. 怎样看懂Oracle的执行计划

    怎样看懂Oracle的执行计划 一.什么是执行计划 An explain plan is a representation of the access path that is taken when ...

  3. 【ORACLE】记录通过执行Oracle的执行计划查询SQL脚本中的效率问题

    记录通过执行Oracle的执行计划查询SQL脚本中的效率问题   问题现象: STARiBOSS5.8.1R2版本中,河北对帐JOB执行时,无法生成发票对帐文件.   首先,Quartz表达式培植的启 ...

  4. oracle 监控执行的sql语句

    oracle 监控执行的sql语句 select * from v$sqlarea a where module='PL/SQL Developer' order by a.FIRST_LOAD_TI ...

  5. (转)Oracle定时执行计划任务

    Oracle定时执行计划任务 在日常工作中,往往有些事情是需要经常重复地做的,例如每天更新业务报表.每天从数据库中提取符合条件的数据.每天将客户关系管理系统中的数据分配给员工做数据库营销……因此我们就 ...

  6. Oracle的执行计划(来自百度文库)

    如何开启oracle执行计划 http://wenku.baidu.com/view/7d1ff6bc960590c69ec37636.html怎样看懂Oracle的执行计划 http://wenku ...

  7. oracle稳定执行计划1

    稳定执行计划 1 策略: Oracle的sql 执行计划在一些场景下会发生变化,导致系统会发生不可知的情况,影响系统的稳定性,特别是关键业务的sql. 比如下面的场景: 统计信息过老,重新收集了统计信 ...

  8. ORACLE的执行计划

    转自:http://www.cnblogs.com/lovingprince/archive/2007/12/07/2166400.html 背景知识:        为了更好的进行下面的内容我们必须 ...

  9. 分析 Oracle SQL 执行计划的关注点

    本文内容摘自<剑破冰山--Oracle开发艺术>一书. 1.判定主要矛盾 在遇到复杂 SQL 语句时,执行计划也非常复杂,往往让人分析起来觉得无从下手,此时应避免顺序解决问题,而是快速定位 ...

随机推荐

  1. Ubuntu常见服务启停

    停止rpcbind服务service portmap stop 停止nfs服务service nfs-kernel-server stop 停止Telnet服务/etc/xinetd.d/telnet ...

  2. 团队协作editconfig与eslint

    editconfig root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf ins ...

  3. Attention Model详解

    要是关注深度学习在自然语言处理方面的研究进展,我相信你一定听说过Attention Model(后文有时会简称AM模型)这个词.AM模型应该说是过去一年来NLP领域中的重要进展之一,在很多场景被证明有 ...

  4. Codeforces Round #604 (Div. 2) B. Beautiful Numbers

    链接: https://codeforces.com/contest/1265/problem/B 题意: You are given a permutation p=[p1,p2,-,pn] of ...

  5. PostgreSQL 抛出错误信息(错误行号)

    抛出错误行号是我们在写SQL中常用到的,在SQL Server和Oracle中都很简单,但是在PostgreSQL怎么实现呢?在网上查了下资料只有pg_exception_context包含错误行,我 ...

  6. ELK实践

    一.ElasticSearch+FileBeat+Kibana搭建平台 在C# 里面运行程序,输出日志(xxx.log 文本文件)到FileBeat配置的路径下面. 平台搭建,参考之前的随笔. Fil ...

  7. golang 无缓冲channel

    golang 无缓冲channel package main import "fmt" func main() { // 1S =1000ms //1ms = 1000us //1 ...

  8. go get 无反应方法 Win/Linux 命令行、终端和 Git 代理设置

    go get -u -v 无反应方法 CMD要用管理员权限运行,否则设置无效 netsh winhttp set proxy proxy-server="https=127.0.0.1:10 ...

  9. [SCOI2005][BZOJ 1084]最大子矩阵

    Description 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大.注意:选出的k个子矩阵不能相互重叠. Input 第一行为n,m,k(1≤n≤100,1≤m≤2 ...

  10. DM-移除几何上的洞方法二

    ​原视频下载地址:http://yunpan.cn/cujkVABuZXc9t  访问密码 ba61