☰
Oracle 等待事件那么多,怎么记才有用?
2026/10/15 7:03:17 网站建设 项目流程

前言

最近在分析一套 Oracle 19c RAC 数据库的性能问题,业务反馈某个生产系统偶尔出现接口超时。

根据业务反馈的异常时间段,抓取了两份 AWR 报告。先看了 DB Time、CPU、Load Profile,整体负载并不高。两个实例的平均 AAS 都在 1 ~ 2 左右,CPU 也比较空闲。

继续往下看,就到了等待事件:

可以看到有很多等待事件:db file sequential read、read by other session、log file sync、gc cr grant 2-way……

Oracle 的等待事件非常多,尤其是 RAC 环境下,各种gc开头的事件,看着差不多,实际含义却不完全一样。

刚开始接触 AWR 时,我也想过把常见的等待事件整理出来,方便平时查阅。但后来发现,单纯记住事件名称和含义,实际帮助并没有想象中那么大。

比如看到db file sequential read,知道它是单块读,但到底是存储响应慢,还是 SQL 读取的数据太多?光知道事件的定义,显然还不够。

所以这篇简单聊聊,我平时是怎么理解和记忆 Oracle 等待事件的。

一、先从名字入手

Oracle 很多等待事件的名称,其实已经告诉了我们大致的排查方向。

关键词大致方向
db file数据文件 I/O
direct path直接路径 I/O
log fileRedo、日志写入、事务提交
enq锁、队列资源争用
gcRAC Global Cache
library cache共享池对象相关
cursor游标相关
read by other session等待其他会话读取数据块

当然,这种分类并不严谨,只是方便平时快速判断。

比如gc current block 2-way,一看gc就知道和 RAC 的 Cache Fusion 有关;enq: TX - row lock contention通常要往事务锁的方向排查;而log file sync,则主要关注事务提交和 Redo 写入。

这样至少能先确定排查方向,至于具体的等待机制、参数含义,遇到了再进一步查也不迟。

二、I/O 相关的等待,先记这几个

最常见的三个:

db file sequential read db file scattered read direct path read

db file sequential read主要是单块读,常见于索引扫描、ROWID 回表等操作;db file scattered read主要是多块读,通常与全表扫描有关;direct path read则是直接路径读取,大表扫描、并行查询时比较常见。

这几个事件都比较常见,但实际分析时不能只看事件名称。

比如db file sequential read排在 AWR 等待事件前面,很多人第一反应就是磁盘 I/O 有问题,其实不一定。

这次数据库的 AWR 中,两个实例的db file sequential read平均等待时间都不到 1ms,单次 I/O 响应并不慢。但继续分析 SQL 后发现,有些 SQL 每次执行需要读取大量数据块,虽然单次读取很快,累计下来耗时依然很长。

其中一条 SQL 的统计信息如下(数值已做近似处理):

SQL_ID 已隐藏 执行次数 少于 10 次 平均执行时间 超过 100 秒 平均逻辑读 数十万块 平均物理读 十万块以上

SQL 访问的是一张业务明细表,主要根据多个业务条件进行汇总查询。

执行计划如下(对象名称已脱敏):

SORT GROUP BY NOSORT INLIST ITERATOR TABLE ACCESS BY INDEX ROWID BUSINESS_TABLE INDEX RANGE SCAN IDX_BUSINESS_01

从执行计划来看,这条 SQL 虽然走了索引,但单次执行仍然产生了大量逻辑读和物理读。

其中INDEX RANGE SCAN预估返回百万级记录,后续还需要通过 ROWID 回表。这里的行数是优化器估算值,实际扫描了多少,还需要结合执行统计进一步确认。

不过,单从 AWR 的数据来看,这条 SQL 的读取量已经明显偏高。

这也说明,db file sequential read等待较多,并不一定是存储性能问题,也可能是 SQL 本身访问了太多数据块。所以分析 I/O 等待时,除了关注单次 I/O 延迟,还要结合 SQL 的逻辑读、物理读和执行计划一起看。

db file scattered read和direct path read也是一样,重点还是看实际扫描了多少数据,以及这些读取是否必要。

毕竟,走了索引不代表 SQL 一定快,全表扫描也不代表执行计划一定有问题。

三、Commit 相关的等待

这类先熟悉两个就够了:

log file sync log file parallel write

log file sync主要是前台会话等待事务提交完成,log file parallel write则是 LGWR 写入 Redo 日志时产生的等待。

这两个事件通常需要结合起来分析:如果log file sync和log file parallel write的平均等待时间都明显升高,就需要重点检查 Redo 日志写入延迟。

如果log file sync较高,而log file parallel write比较正常,就不能直接判断是存储问题,还需要结合 Commit 次数、LGWR 调度等情况进一步排查。

另外,应用频繁提交事务也值得关注:比如每处理一条记录就执行一次 Commit,即使单次提交耗时不高,累计等待时间也可能比较明显。

因此,分析log file sync时,不能只看累计等待时间,还要结合平均等待时间、提交频率以及log file parallel write的表现,判断究竟是提交过于频繁,还是 Redo 写入本身存在延迟。

四、锁和并发相关的等待

最常见的是:

enq: TX - row lock contention

比如会话 A 更新一条记录后一直没有提交,会话 B 又来修改同一条记录,就可能产生等待。

不过,enq: TX - row lock contention并不一定都是普通的行锁阻塞,也可能涉及唯一键冲突、ITL 资源不足等情况。实际排查时,可以先通过GV$SESSION查看阻塞会话,再结合事务信息、等待模式和 SQL 进一步分析。

另外,还有一个比较常见的等待事件:

read by other session

这个事件和行锁等待不是一回事,它表示当前会话需要读取的数据块正在由其他会话读取,因此需要等待对方完成 I/O。

前面提到的那条高读取量 SQL,在 ASH 中就出现了这类等待。

所以碰到并发等待时,还是要先区分具体的等待资源,enq: TX - row lock contention重点排查事务锁,而read by other session则需要关注并发读取、热点数据块以及相关 SQL 的 I/O 情况。

五、RAC 里面的 gc 等待

在 RAC 环境中,gc开头的等待事件也比较常见,主要与实例之间的 Global Cache(全局缓存)协调有关。

比如:

gc cr block 2-way gc current block 2-way gc cr grant 2-way

这几个等待事件名称比较相似,可以拆开来看:

  • gc:Global Cache,RAC 全局缓存。
  • cr:Consistent Read,一致性读。
  • current:当前版本的数据块。
  • block:通常涉及跨实例数据块传输。
  • grant:主要涉及全局缓存访问授权,不一定需要传输数据块。

至于后面的2-way、3-way,主要用于区分不同的消息交互路径。

需要注意的是,RAC 本身就存在跨实例数据块访问,因此出现 gc 等待并不代表集群有问题。

实际分析时,我一般会结合等待次数、平均等待时间和数据块传输量来判断。如果等待时间明显偏高,再进一步检查相关 SQL、热点对象、跨实例访问情况以及私有网络延迟。

六、等待事件到底怎么看?

前面说了这么多,其实平时看 AWR,我主要关注以下几个指标:

指标含义
Waits等待次数
Time(s)累计等待时间
Avg wait平均每次等待时间
% DB time累计等待时间占 DB Time 的比例

这里需要注意,等待次数多不一定代表问题严重,平均等待时间高也不一定说明对数据库整体影响很大。

比如一个事件发生了 100 万次,平均等待 0.1ms,累计等待时间为 100 秒;另一个事件只发生了 100 次,但平均等待 2 秒,累计等待时间反而达到 200 秒。

因此,分析时需要结合等待次数、累计等待时间和平均等待时间,不能只看其中一个指标。

另外,还要注意 AWR 的统计时间范围。这次数据库的 AWR 覆盖了数小时,两个实例的平均 AAS 都比较低,db file sequential read的平均等待时间也不到 1ms。

但业务反馈的是偶发超时,长时间窗口的平均数据很可能掩盖短时间内的性能波动。

对于这种情况,还需要结合业务异常的具体时间,通过 ASH 查看当时的活跃会话、SQL_ID 和等待事件,进一步确认问题发生在哪个环节。

七、遇到不认识的事件怎么办?

这个很简单,直接查。

Oracle 自己就提供了等待事件信息:

SELECT name, wait_class, parameter1, parameter2, parameter3 FROM v$event_name WHERE name = 'db file sequential read';

查询结果:

NAME WAIT_CLASS PARAMETER1 PARAMETER2 PARAMETER3 ----------------------- ---------- ---------- ---------- ---------- db file sequential read User I/O file# block# blocks

可以查看事件所属的 Wait Class,以及 P1、P2、P3 对应的参数含义。

如果想看看数据库当前累计等待时间较高的事件:

SELECT event, total_waits, ROUND(time_waited_micro / 1000000, 2) time_sec, ROUND( time_waited_micro / NULLIF(total_waits, 0) / 1000, 3 ) avg_wait_ms FROM v$system_event WHERE wait_class <> 'Idle' ORDER BY time_waited_micro DESC FETCH FIRST 20 ROWS ONLY;

下面是部分常见等待事件的示例输出(演示数据,非生产查询结果):

EVENT TOTAL_WAITS TIME_SEC AVG_WAIT_MS ----------------------------- ------------ -------- ----------- db file sequential read 12500000 8750.00 0.700 log file sync 3200000 4800.00 1.500 gc current block 2-way 4800000 2400.00 0.500 log file parallel write 2500000 2500.00 1.000 direct path read 1800000 2700.00 1.500 read by other session 1500000 1200.00 0.800 gc cr block 2-way 2100000 1050.00 0.500

需要注意的是,V$SYSTEM_EVENT记录的是实例启动以来的累计等待统计,并不是指定时间段的数据。如果要分析某个历史时段的性能问题,还是需要结合 AWR、ASH 进行排查。

对于不熟悉的等待事件,可以先通过V$EVENT_NAME查看事件分类和等待参数,再结合具体 SQL、会话及执行计划分析。平时遇到有代表性的案例,也可以顺手记录一下,后续碰到类似问题直接参考即可。

最后

Oracle 等待事件虽然很多,但平时真正经常遇到的也就那么一些。像 I/O、Commit、锁和 RAC 相关的等待,结合实际故障多分析几次,基本就熟悉了。

其他不常见的事件,遇到了再查也不迟。关键还是要结合等待时间、SQL 和执行计划,判断问题到底出在哪里,而不是单纯根据等待事件的名称下结论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询