诊断某段时间数据库性能抖动问题思路

场景:

经常有开发人员反馈,在某段时间(甚至时间精确到秒) 反馈有业务接口超时问题。并不一定是某条sql慢,而是整个应用接口上都出现了慢sql问题。这时候一般是根据精确的时间点观察等待事件和报错日志以及时间点前后数据库在干什么事情来分析问题。

查看错误日志信息+监控信息

重点查看的是数据库alert日志错误告警和操作系统错误日志信息:

linux下举例:/u01/app/oracle/diag/rdbms/orcl11g/orcl11g/trace/alert_orcl11g.log

/var/log/message

主要查看ORA-xxx ,error, faild,warning等关键错误信息。

问题时段监控信息:自由度比较大,依赖数据库外部监控系统

user cpu、sys cpu、QPS&TPS、活跃会话信息、阻塞会话信息、逻辑读、物理读、磁盘IOPS等信息

OSwatcher信息作为辅助

对于oracle瞬间抖动的性能问题,一般通过以上说的错误日志和监控信息是看不错啥结果的,基于Oracle强大的性能数据收集功能,可以通过AWR+ASH这两个工具来分析具体某个时间段甚至时间点,基于等待事件的方法论来分析数据库那个时间段在干什么,性能瓶颈在哪儿。

利用AWR分析数据库负载以及主要性能瓶颈

适用场景:

数据库整体性能问题出现的时间稍长,比如超过5分钟。我们的AWR报告生成间隔是15分钟,虽然15分钟的跨度会把某些指标平均掉,但对于超过5分钟的性能问题,awr报告还是可以捕捉到很多有用的性能指标。

看整体负载情况

看DB Time:

image.png

如上,awr报告开头部分会看到数据库实例、操作系统的一些关键信息,可以一眼看到数据库跑在一个什么样的环境中。

这里的CPU数是指逻辑CPU数(Number of logical CPUs available (includeshardwarethreads if hardwarethreadingis turned on))

当硬件线程技术打开时,例如:POWERSMT(SimultaneousMulti-Thread) 或者IntelHT(HyperThread)。此时一个CPU core被视作2个或更多个逻辑CPU。

紧接着一个重要的指标就是DB Time,一个经验值就是如果DB Time大于 elapsed time * cores,一般代表这数据库负载比较高,这份awr报告是值得仔细分析的。如果没有超过这个数,一般都是极个别SQL性能差引起的,对整体负载并没有引起太大的波动。上图中15*32 = 480 mins,DB Time:2333.51 mins,很值得往下深入分析。

补充:DB Time是怎么算出来的

DB TIME= DB CPU + Non-Idle Wait + Wait on CPU queue

  • Totaltime in database calls by foreground sessions
  • IncludesCPU time, IO time and non-idle wait time
  • DBTime <> response time
  • TotalDB time = sum of DB time for all active sessions

000.png

看load profile:

image.png

load profile主要反映的是在awr快照间隔内,数据库在干活的状态下,各种度量指标,可以从QPS(Executes SQL)和TPS(Transactions)上看出数据库对业务sql响应的大概情况。

下面 是各具体指标的解析:

指标

指标含义

redo size

单位 bytes,redo size可以用来估量update/insert/delete的频率,大的redo size往往对lgwr写日志,和arch归档造成I/O压力, Per Transaction可以用来分辨是 大量小事务, 还是少量大事务。如上例每秒redo 约7.5MB ,每个事务3KB,基本可以判断系统处于业务高峰期,批量处理的事物比较多。

Logical Read

单位 次数*块数, 相当于 “人*次”, 如上例 112550.2 * db_block_size(8k)=879MB/s , 逻辑读耗CPU,主频和CPU核数都很重要,逻辑读高则DB CPU往往高,也往往可以看到latch: cache buffer chains等待。但这不是最高,有些繁忙OLTP系统可以高达几十乃至上百Gbytes。

Block changes

单位 次数*块数 ,描述数据变化频率

Physical Read

单位次数*块数, 如上例 14.3 * 8k = 114.4B/s,物理读偏低,也说明该系统select比较少,主要以DML为主。 物理读消耗IO读,体现在IOPS和吞吐量等不同纬度上;但减少物理读可能意味着消耗更多CPU。好的存储 每秒物理读能力达到几GB。  这个physical read包含了physical reads cache和physical reads direct

Physical writes

单位  次数*块数,主要是DBWR写datafile,也有direct path write。 dbwr长期写出慢会导致定期log file switch(checkpoint no complete) 检查点无法完成的前台等待。  这个physical write 包含了physical writes direct +physical writes from cache

User Calls

单位次数,用户调用数

Parses

解析次数,包括软软解析+软解析+硬解析,软解析优化得不好,则夸张地说几乎等于每秒SQL执行次数。 即执行解析比1:1,而我们希望的是 解析一次 到处运行。

Hard Parses

硬解析是万恶之源。伴随着大量Cursor pin s on X, library cache: mutex X , latch: row cache objects /shared pool等。 硬解析最好少于每秒20次

W/A MB processed

单位MB  W/A workarea  workarea中处理的数据数量 结合 In-memory Sort%, sorts (disk) PGA Aggr一起看

Logons

登陆次数, logon storm 登陆风暴,结合AUDIT审计数据一起看。短连接的附带效应是游标缓存(session cached cursor)无用

Executes

执行次数,反映的是执行频率

Rollback

回滚次数, 反映回滚频率,作为性能参考指标

Transactions

每秒事务数,是数据库层的TPS,可以看做压力测试或比对性能时的一个指标,需要综合的去看才有意义

看命中率

Instance Efficiency Percentages (Target 100%)

还是贴成表格方便些

Buffer Nowait %: 99.78 Redo NoWait %: 100.00
Buffer Hit %: 100.00 In-memory Sort %: 100.00
Library Hit %: 99.96 Soft Parse %: 100.00
Execute to Parse %: 0.08 Latch Hit %: 99.71
Parse CPU to Parse Elapsd %: 52.91 % Non-Parse CPU: 95.06

上面表格中标红的指标的值越高越好。指标Parse CPU to Parse Elapsd的值比较低,说明该系统解析情况并不是很好。

Parse CPU to Parse Elapsd:解析实际运行时间/(解析实际运行时间+解析中等待资源时间),越高越好。计算公式为:Parse CPU to Parse Elapsd %=100*(parse time cpu / parse time elapsed)。即:解析实际运行时间/(解析实际运行时间+解析中等待资源时间)。如果该比率为 100%,意味着 CPU 等待时间为 0,没有任何等待。

Non-Parse CPU :SQL 实际运行时间/(SQL 实际运行时间+SQL 解析时间),太低表示解析消耗时间过多。计算公式为:% Non-Parse CPU=round(100*1-PARSE_CPU/TOT_CPU),2)。

如果这个值比较小,表示解析消耗的 CPU 时间过多。与 PARSE_CPU 相比,如果 TOT_CPU 很高,这个比值将接近 100%,这是很好的,说明计算机执行的大部分工作是执行查询的工作,而

不是分析查询的工作。

看TOP等待事件

Top 10 Foreground Events by Total Wait Time

Event Waits Total Wait Time (sec) Wait Avg(ms) % DB time Wait Class
log file sync 1,241,679 94.6K 76 67.6 Commit
library cache: mutex X 27,048,671 34.7K 1 24.8 Concurrency
reliable message 4,200,609 4092.4 1 2.9 Other
DB CPU   3787.1   2.7  
enq: TX - index contention 84,828 372.2 4 .3 Concurrency
latch: cache buffers chains 65,945 301.1 5 .2 Concurrency
buffer busy waits 212,212 297.9 1 .2 Concurrency
latch: ges resource hash list 22,526 269.6 12 .2 Other
enq: TX - row lock contention 3,397 70.3 21 .1 Application
cursor: pin S 12,956 58.8 5 .0 Concurrency

top等待事件是很重要的指标,显示了那些可能构成系统瓶颈的数据库等待事件。

首先检查等待类是否为用户I/O,系统I/O等等,但如果等待类是并发性(Concurrency),那么可能会有一些严重的问题。

接下来要看的是Total Wait Time(sec),它显示了DB在这个类中等待了多少次,然后是Wait Avg(ms)。如果总等待时间高,但等待平均时间(ms)低,那么可以忽略这个。如果两者都是高的或平均等待是高的,则必须进行深入分析。

对于最大的等待,比如上面的log file sync等待,查看该等待事件直方图(Wait Event Histogram)以确定等待的分布。

该案例展示了,数据库性能瓶颈在于log file sync,并且平均等待时间比较长,接下来就要去看等待事件直方图。

从top等待事件分析问题的基础在于熟悉每个等待事件指标的含义,等待事件具体代表啥请移步oracle官方MOS...

看等待事件直方图(Wait Event Histogram)

image.png

image.png

分析:从64ms ~ 2sec 的直方图中看出,99.2%的等待都是小于32ms的,这可能看不出啥问题,接着往下看。

4sec 2min的等待直方图中看出,99.9%的都是小于2秒的,但是4s ~ 2min的等待却有1485个。对于log file sync等待事件来说,写日志等了这么久,反映在前端等待事件上。肯定会有严重问题的。最后也证实了因为存储光纤链路抖动导致。

看TOP SQL

Oracle数据库中80%的问题都是SQL问题引起的,TOP SQL也是比较重要的指标,但也有可能awr快照范围没覆盖某些问题sql,需要综合的去分析。

image.png

sql order by elapsed time 主要看执行总时间以及执行次数,如果平均每次执行的时间很短,可以忽略掉。

其它类似指标也是同样道理,如果sql单次执行时间很长,又跑在OLTP系统上,要么优化掉,要么挪到读库去执行,不能损耗写库宝贵的性能资源。

但是平均执行时间短,但是执行次数非常频繁的SQL也要引起重视,因为高频次SQL稍微一抖动,就会引起阻塞风暴,拖垮系统性能。

利用ASH分析等待事件以及阻塞源头

适用场景

在很多情况下,数据库发生性能问题持续的时间比较短,比如持续了1秒 ~ 15秒。前面提供的几种手段可能并不凑效。比如监控粒度粗(分钟级别),AWR间隔长(15分钟,平均掉关键信息)。可以通过ash来分析问题。

关于gv$active_session_history和dba_hist_active_sess_history:

从Oracle 10G开始,引入了AWR和ASH采样机制,有一个视图gv$active_session_history会每秒钟将数据库所有节点的Active Session采样一次,而dba_hist_active_sess_history则会将gv$active_session_history里的数据每10秒采样一次并持久化保存。但是比基于这个特征,我们可以通过分析dba_hist_active_sess_history的Session采样情况,来定位问题发生的准确时间范围,并且可以观察每个采样点的top event和top holder。

gv$active_session_history虽然没有持久化,但是比dba_hist_active_sess_history保存的信息要全面。所以,在必要的时候备份下

create table ash_xxxx tablespace tbs_xxx as select * from gv$active_session_history 
where SAMPLE_TIME between TO_TIMESTAMP ('<time_begin>', 'YYYY-MM-DD HH24:MI:SS') and TO_TIMESTAMP ('<time_end>', 'YYYY-MM-DD HH24:MI:SS');

通过ash速查找阻塞源头

脚本一:某比较短时间内等待次数统计,分钟级别和秒级别

从问题发生点前后的某几秒时间内观察所有会话的等待事件,可以判断某会话在等待啥甚至是谁阻塞它,阻塞者(罪魁祸首)是后台进程还是前端SQL会话都可以看出来。后面要做的事情就是从gv$active_session_history 找到对应session_id 甚至对应sql_id 去解决。

col sample_time for a30 
col event for a60 
col count(1) for 999999 
select to_char(trunc(sample_time,'mi'),'yyyy-mm-dd hh24:mi:ss') sample_time,event,count(1) 
from gv$active_session_history  
where sample_time >=to_date('&begin_time','yyyy-mm-dd hh24:mi:ss')  
and sample_time <=to_date('&end_time','yyyy-mm-dd hh24:mi:ss')  
group by trunc(sample_time,'mi'),event order by 1; 

--秒级别
select to_char(sample_time,'yyyy-mm-dd hh24:mi:ss') sample_time,event,count(1) 
from gv$active_session_history  
where sample_time >=to_date('&begin_time','yyyy-mm-dd hh24:mi:ss')  
and sample_time <=to_date('&end_time','yyyy-mm-dd hh24:mi:ss')  
group by to_char(sample_time,'yyyy-mm-dd hh24:mi:ss'),event order by 1; 

脚本二:从v$active_session_history中查看某段时间阻塞链

with ash as (select inst_id,SESSION_ID,event,BLOCKING_SESSION,program ,
to_char(SAMPLE_TIME,'YYYYMMDD HH24MISS') SAMPLE_TIME,sample_id,blocking_inst_id
from v$active_session_history where
sample_time >to_date('2020-06-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
      and 
      to_char(sample_time,'hh24:mi:ss') between '06:00:00' and '06:10:00')      
select SAMPLE_TIME,FINAL_BLK,FINAL_PROGRAM,
nvl(FINAL_EVT,'ON CPU') as FINAL_EVT
,(LVL2_EVENT) as LVL2_EVENT,LVL3_EVENT,LVL4_EVENT,
count(*) from(
select SESSION_ID,SAMPLE_TIME,sys_connect_by_path(SESSION_ID,',') CHAIN,
connect_by_root(PROGRAM)as FINAL_PROGRAM,
connect_by_root(SESSION_ID)FINAL_BLK,
connect_by_root(event) FINAL_EVT,event ,
case when level=2 then event end as LVL2_EVENT ,
case when level=3 then event end as LVL3_EVENT ,
case when level=4 then event end as LVL4_EVENT
from ash start with BLOCKING_SESSION is null
connect by prior SESSION_ID = BLOCKING_SESSION and prior inst_id= BLOCKING_INST_ID and sample_id = prior sample_id
) group by FINAL_BLK,FINAL_EVT,SAMPLE_TIME,FINAL_PROGRAM ,LVL3_EVENT,LVL4_EVENT,LVL2_EVENT
having count(*) > 1
order by SAMPLE_TIME,count(*) desc
/

有时候通过上面快捷的脚本并没有立马找到问题根源,漏掉了一些关键细节信息。所以还需要进一步的分析原始的v$active_session_history信息,下面的章节步骤

通过ash逐步定位性能问题(详细)

oracle 官方论坛给出的方法,可以清楚的看到如何通过ash一步步缩小问题范围并定位问题,参考地址如下:

https://blogs.oracle.com/Database4CN/entry/如何通过dba_hist_active_sess_history分析历史数据库性能问题

Dump出问题期间的ASH数据:

为了不影响生产系统,我们可以将问题大概期间的ASH数据export出来在测试机上分析。

基于dba_hist_active_sess_history创建一个新表m_ash,然后将其通过exp/imp导入到测试机。在发生问题的数据库上执行exp:

SQL> conn user/passwd

SQL> create table m_ash as select * from dba_hist_active_sess_history 
where SAMPLE_TIME between TO_TIMESTAMP ('<time_begin>', 'YYYY-MM-DD HH24:MI:SS') and TO_TIMESTAMP ('<time_end>', 'YYYY-MM-DD HH24:MI:SS');

$ exp user/passwd file=m_ash.dmp tables=(m_ash) log=m_ash.exp.log

然后导入到测试机:

$ imp user/passwd file=m_ash.dmp log=m_ash.imp.log

验证导出的ASH时间范围:

为了加快速度,我们采用了并行查询。另外建议采用Oracle SQL Developer来查询以防止输出结果折行不便于观察。

set line 200 pages 1000
col sample_time for a25
col event for a40
alter session set nls_timestamp_format='yyyy-mm-dd hh24:mi:ss.ff';
select /*+ parallel 8 */
 t.dbid, t.instance_number, min(sample_time), max(sample_time), count(*) session_count
  from m_ash t
 group by t.dbid, t.instance_number
 order by dbid, instance_number;

结果如下:

INSTANCE_NUMBER    MIN(SAMPLE_TIME)    MAX(SAMPLE_TIME)    SESSION_COUNT

1    2015-03-26 21:00:04.278    2015-03-26 22:59:48.387    2171

2    2015-03-26 21:02:12.047    2015-03-26 22:59:42.584    36

从以上输出可知该数据库共2个节点,采样时间共2小时,节点1的采样比节点2要多很多,问题可能发生在节点1上。

确认问题发生的精确时间范围:

参考如下脚本:

select /*+ parallel 8 */
 dbid, instance_number, sample_id, sample_time, count(*) session_count
  from m_ash t
 group by dbid, instance_number, sample_id, sample_time
 order by dbid, instance_number, sample_time;

结果如下:

INSTANCE_NUMBER    SAMPLE_ID    SAMPLE_TIME    SESSION_COUNT
1    36402900    2015-03-26 22:02:50.985    4
1    36402910    2015-03-26 22:03:01.095    1
1    36402920    2015-03-26 22:03:11.195    1
1    36402930    2015-03-26 22:03:21.966    21
1    36402940    2015-03-26 22:03:32.116    102
1    36402950    2015-03-26 22:03:42.226    181
1    36402960    2015-03-26 22:03:52.326    200
1    36402970    2015-03-26 22:04:02.446    227
1    36402980    2015-03-26 22:04:12.566    242
1    36402990    2015-03-26 22:04:22.666    259
1    36403000    2015-03-26 22:04:32.846    289
1    36403010    2015-03-26 22:04:42.966    147
1    36403020    2015-03-26 22:04:53.076    2
1    36403030    2015-03-26 22:05:03.186    4
1    36403040    2015-03-26 22:05:13.296    1
1    36403050    2015-03-26 22:05:23.398    1

注意观察以上输出的每个采样点的active session的数量,数量突然变多往往意味着问题发生了。从以上输出可以确定问题发生的精确时间在2015-03-26 22:03:21 ~ 22:04:42,问题持续了大约1.5分钟。

注意: 观察以上的输出有无断档,比如某些时间没有采样。

确定每个采样点的top n event:

在这里我们指定的是top 2 event并且注掉了采样时间以观察所有采样点的情况。如果数据量较多,您也可以通过开启sample_time的注释来观察某个时间段的情况。注意最后一列session_count指的是该采样点上的等待该event的session数量。

select t.dbid,
       t.sample_id,
       t.sample_time,
       t.instance_number,
       t.event,
       t.session_state,
       t.c session_count
  from (select t.*,
               rank() over(partition by dbid, instance_number, sample_time order by c desc) r
          from (select /*+ parallel 8 */
                 t.*,
                 count(*) over(partition by dbid, instance_number, sample_time, event) c,
                 row_number() over(partition by dbid, instance_number, sample_time, event order by 1) r1
                  from m_ash t
                /*where sample_time >
                    to_timestamp('2013-11-17 13:59:00',
                                 'yyyy-mm-dd hh24:mi:ss')
                and sample_time <
                    to_timestamp('2013-11-17 14:10:00',
                                 'yyyy-mm-dd hh24:mi:ss')*/
                ) t
         where r1 = 1) t
 where r < 3
 order by dbid, instance_number, sample_time, r;

结果如下:

SAMPLE_ID    SAMPLE_TIME    INSTANCE_NUMBER    EVENT    SESSION_STATE    SESSION_COUNT
36402900    22:02:50.985    1        ON CPU    3
36402900    22:02:50.985    1    db file sequential read    WAITING    1
36402910    22:03:01.095    1        ON CPU    1
36402920    22:03:11.195    1    db file parallel read    WAITING    1
36402930    22:03:21.966    1    cursor: pin S wait on X    WAITING    11
36402930    22:03:21.966    1    latch: shared pool    WAITING    4
36402940    22:03:32.116    1    cursor: pin S wait on X    WAITING    83
36402940    22:03:32.116    1    SGA: allocation forcing component growth    WAITING    16
36402950    22:03:42.226    1    cursor: pin S wait on X    WAITING    161
36402950    22:03:42.226    1    SGA: allocation forcing component growth    WAITING    17
36402960    22:03:52.326    1    cursor: pin S wait on X    WAITING    177
36402960    22:03:52.326    1    SGA: allocation forcing component growth    WAITING    20
36402970    22:04:02.446    1    cursor: pin S wait on X    WAITING    204
36402970    22:04:02.446    1    SGA: allocation forcing component growth    WAITING    20
36402980    22:04:12.566    1    cursor: pin S wait on X    WAITING    219
36402980    22:04:12.566    1    SGA: allocation forcing component growth    WAITING    20
36402990    22:04:22.666    1    cursor: pin S wait on X    WAITING    236
36402990    22:04:22.666    1    SGA: allocation forcing component growth    WAITING    20
36403000    22:04:32.846    1    cursor: pin S wait on X    WAITING    265
36403000    22:04:32.846    1    SGA: allocation forcing component growth    WAITING    20
36403010    22:04:42.966    1    enq: US - contention    WAITING    69
36403010    22:04:42.966    1    latch: row cache objects    WAITING    56
36403020    22:04:53.076    1    db file scattered read    WAITING    1
36403020    22:04:53.076    1    db file sequential read    WAITING    1

从以上输出我们可以发现问题期间最严重的等待为cursor: pin S wait on X,高峰期等待该event的session数达到了265个,其次为SGA: allocation forcing component growth,高峰期session为20个。

注意:

  1. 再次确认以上输出有无断档,是否有某些时间没有采样。
  2. 注意那些session_state为ON CPU的输出,比较ON CPU的进程个数与您的OS物理CPU的个数,如果接近或者超过物理CPU个数,那么您还需要检查OS当时的CPU资源状况,比如OSWatcher/NMON等工具,高的CPU Run Queue可能引发该问题,当然也可能是问题的结果,需要结合OSWatcher和ASH的时间顺序来验证。

观察每个采样点的等待链:

其原理为通过dba_hist_active_sess_history. blocking_session记录的holder来通过connect by级联查询,找出最终的holder. 在RAC环境中,每个节点的ASH采样的时间很多情况下并不是一致的,因此您可以通过将本SQL的第二段注释的sample_time稍作修改让不同节点相差1秒的采样时间可以比较(注意最好也将partition by中的sample_time做相应修改)。该输出中isleaf=1的都是最终holder,而iscycle=1的代表死锁了(也就是在同一个采样点中a等b,b等c,而c又等a,这种情况如果持续发生,那么尤其值得关注)。采用如下查询能观察到阻塞链。

select /*+ parallel 8 */
 level                     lv,
 connect_by_isleaf         isleaf,
 connect_by_iscycle        iscycle,
 t.dbid,
 t.sample_id,
 t.sample_time,
 t.instance_number,
 t.session_id,
 t.sql_id,
 t.session_type,
 t.event,
 t.session_state,
 t.blocking_inst_id,
 t.blocking_session,
 t.blocking_session_status
  from m_ash t
/*where sample_time >
    to_timestamp('2013-11-17 13:55:00',
                 'yyyy-mm-dd hh24:mi:ss')
and sample_time <
    to_timestamp('2013-11-17 14:10:00',
                 'yyyy-mm-dd hh24:mi:ss')*/
 start with blocking_session is not null
connect by nocycle
 prior dbid = dbid
       and prior sample_time = sample_time
          /*and ((prior sample_time) - sample_time between interval '-1'
          second and interval '1' second)*/
       and prior blocking_inst_id = instance_number
       and prior blocking_session = session_id
       and prior blocking_session_serial# = session_serial#
 order siblings by dbid, sample_time;

结果如下:

LV    ISLEAF    ISCYCLE    SAMPLE_TIME    INSTANCE_NUMBER    SESSION_ID    SQL_ID    EVENT    SESSION_STATE    BLOCKING_INST_ID    BLOCKING_SESSION    BLOCKING_SESSION_STATUS

1    0    0    22:04:32.846    1    1259    3ajt2htrmb83y    cursor:    WAITING    1    537    VALID

2    1    0    22:04:32.846    1    537    3ajt2htrmb83y    SGA:    WAITING            UNKNOWN

注意,为了输出便于阅读,我们将等待event做了简写,下同。从上面的输出可见,在相同的采样点上(22:04:32.846),节点1 session 1259在等待cursor: pin S wait on X,其被节点1 session 537阻塞,而节点1 session 537又在等待SGA: allocation forcing component growth,并且ASH没有采集到其holder,因此这里cursor: pin S wait on X只是一个表面现象,问题的原因在于SGA: allocation forcing component growth

基于第5步的原理来找出每个采样点的最终top holder:

比如如下SQL列出了每个采样点top 2的blocker session,并且计算了其最终阻塞的session数(参考blocking_session_count)

select t.lv,
       t.iscycle,
       t.dbid,
       t.sample_id,
       t.sample_time,
       t.instance_number,
       t.session_id,
       t.sql_id,
       t.session_type,
       t.event,
       t.seq#,
       t.session_state,
       t.blocking_inst_id,
       t.blocking_session,
       t.blocking_session_status,
       t.c blocking_session_count
  from (select t.*,
               row_number() over(partition by dbid, instance_number, sample_time order by c desc) r
          from (select t.*,
                       count(*) over(partition by dbid, instance_number, sample_time, session_id) c,
                       row_number() over(partition by dbid, instance_number, sample_time, session_id order by 1) r1
                  from (select /*+ parallel 8 */
                         level              lv,
                         connect_by_isleaf  isleaf,
                         connect_by_iscycle iscycle,
                         t.*
                          from m_ash t
                        /*where sample_time >
                            to_timestamp('2013-11-17 13:55:00',
                                         'yyyy-mm-dd hh24:mi:ss')
                        and sample_time <
                            to_timestamp('2013-11-17 14:10:00',
                                         'yyyy-mm-dd hh24:mi:ss')*/
                         start with blocking_session is not null
                        connect by nocycle
                         prior dbid = dbid
                               and prior sample_time = sample_time
                                  /*and ((prior sample_time) - sample_time between interval '-1'
                                  second and interval '1' second)*/
                               and prior blocking_inst_id = instance_number
                               and prior blocking_session = session_id
                               and prior
                                    blocking_session_serial# =session_serial#) t
                 where t.isleaf = 1) t
         where r1 = 1) t
 where r < 3
 order by dbid, sample_time, r;

结果如下:

SAMPLE_TIME    INSTANCE_NUMBER    SESSION_ID    SQL_ID    EVENT    SEQ#    SESSION_STATE    BLOCKING_SESSION_STATUS    BLOCKING_SESSION_COUNT
22:03:32.116    1    1136    1p4vyw2jan43d    SGA:    1140    WAITING    UNKNOWN    82
22:03:32.116    1    413    9g51p4bt1n7kz    SGA:    7646    WAITING    UNKNOWN    2
22:03:42.226    1    1136    1p4vyw2jan43d    SGA:    1645    WAITING    UNKNOWN    154
22:03:42.226    1    537    3ajt2htrmb83y    SGA:    48412    WAITING    UNKNOWN    4
22:03:52.326    1    1136    1p4vyw2jan43d    SGA:    2150    WAITING    UNKNOWN    165
22:03:52.326    1    537    3ajt2htrmb83y    SGA:    48917    WAITING    UNKNOWN    8
22:04:02.446    1    1136    1p4vyw2jan43d    SGA:    2656    WAITING    UNKNOWN    184
22:04:02.446    1    537    3ajt2htrmb83y    SGA:    49423    WAITING    UNKNOWN    10
22:04:12.566    1    1136    1p4vyw2jan43d    SGA:    3162    WAITING    UNKNOWN    187
22:04:12.566    1    2472        SGA:    1421    WAITING    UNKNOWN    15
22:04:22.666    1    1136    1p4vyw2jan43d    SGA:    3667    WAITING    UNKNOWN    193
22:04:22.666    1    2472        SGA:    1926    WAITING    UNKNOWN    25
22:04:32.846    1    1136    1p4vyw2jan43d    SGA:    4176    WAITING    UNKNOWN    196
22:04:32.846    1    2472        SGA:    2434    WAITING    UNKNOWN    48

注意以上输出,比如第一行,代表在22:03:32.116,节点1的session 1136最终阻塞了82个session.  顺着时间往下看,可见节点1的session 1136是问题期间最严重的holder,它在每个采样点都阻塞了100多个session,并且它持续等待SGA: allocation forcing component growth,注意观察其seq#您会发现该event的seq#在不断变化,表明该session并未完全hang住,由于时间正好发生在夜间22:00左右,这显然是由于自动收集统计信息job导致shared memory resize造成,因此可以结合Scheduler/MMAN的trace以及dba_hist_memory_resize_ops的输出进一步确定问题。

注意:

  1. blocking_session_count 指某一个holder最终阻塞的session数,比如 a <- b<- c (a被b阻塞,b又被c阻塞),只计算c阻塞了1个session,因为中间的b可能在不同的阻塞链中发生重复。
  2. 如果最终的holder没有被ash采样(一般因为该holder处于空闲),比如 a<- c 并且b<- c (a被c阻塞,并且b也被c阻塞),但是c没有采样,那么以上脚本无法将c统计到最终holder里,这可能会导致一些遗漏。
  3. 注意比较blocking_session_count的数量与第3步查询的每个采样点的总session_count数,如果每个采样点的blocking_session_count数远小于总session_count数,那表明大部分session并未记载holder,因此本查询的结果并不能代表什么。
  4. 在Oracle 10g中,ASH并没有blocking_inst_id列,在以上所有的脚本中,您只需要去掉该列即可,因此10g的ASH一般只能用于诊断单节点的问题。

其他关于ASH的应用

除了通过ASH数据来找holder以外,我们还能用它来获取很多信息(基于数据库版本有所不同):

比如通过PGA_ALLOCATED列来计算每个采样点的最大PGA,合计PGA以分析ora-4030/Memory Swap相关问题;

通过TEMP_SPACE_ALLOCATED列来分析临时表空间使用情况;

通过IN_PARSE/IN_HARD_PARSE/IN_SQL_EXECUTION列来分析SQL处于parse还是执行阶段;

通过CURRENT_OBJ#/CURRENT_FILE#/CURRENT_BLOCK#来确定I/O相关等待发生的对象.

猜你喜欢

转载自blog.csdn.net/u010033674/article/details/107068553
今日推荐