等待事件的源起
等待事件的概念大概是从ORACLE 7.0.12中引入的,大致有100个等待事件。在ORACLE 8.0中这个数目增大到了大约150个,在ORACLE 8I中大约有220个事件,在ORACLE 9IR2中大约有400个等待事件,而在最近ORACLE 10GR2中,大约有874个等待事件。
虽然不同版本和组件安装可能会有不同数目的等待事件,但是这些等待事件都可以通过查询V$EVENT_NAME视图获得:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 – Production
SQL> select count(*) from v$event_name;
COUNT(*)
----------
872
ORACLE的等待事件,主要可以分为两类,即空闲(IDLE)等待事件和非空闲(NON-IDLE)等待事件。
1). 空闲等待事件指ORACLE正等待某种工作,在诊断和优化数据库的时候,不用过多注意这部分事件。
2). 非空闲等待事件专门针对ORACLE的活动,指数据库任务或应用运行过程中发生的等待,这些等待事件是在调整数据库的时候需要关注与研究的。
下面来看一下ORACLE 10GR2中主要分类及各类等待事件的个数:
SQL> select wait_class#,wait_class_id,wait_class,count(*) as "count"
2 from v$event_name
3 group by wait_class#,wait_class_id,wait_class
4 order by wait_class#;
WAIT_CLASS# WAIT_CLASS_ID WAIT_CLASS count
----------- ------------- ------------------------------ ----------
0 1893977003 Other 588
1 4217450380 Application 12
2 3290255840 Configuration 23
3 4166625743 Administrative 46
4 3875070507 Concurrency 24
5 3386400367 Commit 1
6 2723168908 Idle 62
7 2000153315 Network 26
8 1740759767 User I/O 17
9 4108307767 System I/O 24
10 2396326234 Scheduler 2
11 3871361733 Cluster 47
12 rows selected.
常见的空闲事件有:
• dispatcher timer
• lock element cleanup
• Null event
• parallel query dequeue wait
• parallel query idle wait - Slaves
• pipe get
• PL/SQL lock timer
• pmon timer- pmon
• rdbms ipc message
• slave wait
• smon timer
• SQL*Net break/reset to client
• SQL*Net message from client
• SQL*Net message to client
• SQL*Net more data to client
• virtual circuit status
• client message
一些常见的非空闲等待事件有:
• db file scattered read
• db file sequential read
• buffer busy waits
• free buffer waits
• enqueue
• latch free
• log file parallel write
• log file sync
几个视图的总结:
V$SESSION 代表数据库活动的开始,视为源起。
V$SESSION_WAIT 视图用以实时记录活动SESSION的等待情况,是当前信息。
V$SESSION_WAIT_HISTORY 是对V$SESSION_WAIT的简单增强,记录活动SESSION的最近10次等待。
V$ACTIVE_SESSION_HISTORY 是ASH的核心,用以记录活动SESSION的历史等待信息,每秒采样一次,这部分内容记录在内存中,期望值是记录一个小时的内容。
WRH#_ACTIVE_SESSION_HISTORY 是V$ACTIVE_SESSION_HISTORY在AWR的存储地。
V$ACTIVE_SESSION_HISTORY中的信息会被定期(每小时一次)的刷新到负载库中,并缺省保留一个星期用于分析。
DBA_HIST_ACTIVE_SESS_HISTORY视图是WRH#_ACTIVE_SESSION_HISTORY视图和其他几个视图的联合展现,通常通过这个视图进行历史数据的访问。
V$SYSTEM_EVENT 由于V$SESSION记录的是动态信息,和SESSION的生命周期相关,而并不记录历史信息,所以ORACLE提供视图V$SYSTEM_EVENT来记录数据库自启动以来所有等待事件的汇总信息。通过这个视图,用户可以迅速获得数据库运行的总体概况。
V$SQLTEXT 当数据库出现瓶颈时,通常可以从V$SESSION_WAIT找到那些正在等待资源的SESSION,通过SESSION的SID,联合V$SESSION和V$SQLTEXT视图就可以捕获这些SESSION正在执行的SQL语句。
重要等待事件
1. Db file sequential read(数据文件顺序读取)
Db file sequential read是个非常常见的I/O相关的等待事件,通常显示与单个数据块相关的读取操作,在大多数情况下,读取一个索引块或者通过索引读取一个数据块时,都会记录这个等待。
这个等待事件有3个参数P1、P2、P3,其中P1代表Oracle要读取的文件的绝对文件号,P2代表Oracle从这个文件中开始读取的起始数据块块号,P3代表读取的Block数量,通常这个值为1,表明是单个Block被读取。
SQL> select name,parameter1,parameter2,parameter3 from v$event_name where name='db file sequential read';
NAME PARAMETER1 PARAMETER2 PARAMETER3
------------------------------ ---------- ---------- ----------
db file sequential read file# block# blocks
如果这个等待事件比较显著,可能表示在多表连接中,表的连接顺序存在问题,可能没有正确的使用驱动表;或者可能索引的使用存在问题,并非索引总是最好的选择。
在大多数情况下,通过索引可以更为快速地获取记录,所以对于一个编码规范、调整良好的数据库,这个等待事件很大通常是正常的。但是在很多情况下,使用索引并不是最佳的选择,比如读取较大表中大量的数据,全表扫描可能会明显快于索引扫描,所以在开发中就应该注意,对于这样的查询应该避免使用索引扫描。
从Oracle 9iR2开始,Oracle引入了段级统计信息收集的新特性,收集的统计信息共有11类:
Select * from v$segstat_name;
在Oracle 10gR2中,这类统计信息增加为15个。
对于CBO模式下的数据库,应当及时收集统计信息,使SQL可以选择正确的执行计划,避免因为统计信息陈旧而导致的执行错误等。
2. Db file scattered read(数据文件离散读取)
SQL> select * from v$event_name where name='db file scattered read';
EVENT# EVENT_ID NAME PARAMETER1 PARAMETER2 PARAMETER3
---------- ---------- ------------------------- ---------- ---------- ----------
188 506183215 db file scattered read file# block# blocks
从V$EVENT_NAME视图可以看到,该事件有3个参数,分别代表文件号、起始数据块号、数据块的数量。
起始数据块号加上数据块的数量,这意味着Oracle session正在等待多块连续读操作的完成。这个操作可能与全表扫描(Full table scan)或者快速全索引扫描(Index Fast Full Scan)的连续读取相关。根据经验,通常大量的db file scattered read等待可能意味着应用问题或者索引缺失。
在实际环境的诊断过程中,可以通过v$session_wait视图发现session的等待,再结合其他视图找到存在问题的SQL等根本原因,从而从根本上解决问题。当这个等待事件比较显著时,也可结合v$session_longops动态性能视图来进行诊断,该视图记录了长时间(运行时间超过6秒的)运行的事务。
从Oracle 9i开始,Oracle新增加了一个视图V$SQL_PLAN用于记录当前系统Library Cache中SQL语句的执行计划,可以通过这个视图找到存在问题的SQL语句。
通过V$SQL_PLAN视图,可以获得大量有用的信息:
获得全表扫描的对象
Select distinct object_name,object_owner from v$sql_plan p Where p.operation='TABLE ACCESS'and p.options='FULL' and object_owner='SYS';
获得全索引扫描的对象
Select distinct object_name,object_owner from v$sql_plan p Where p.operation='INDEX' and p.options='FULL SCAN' and object_owner='SYS';
通过V$SQL_PLAN和V$SQLTEXT联合,获得全表扫描的SQL语句
Select sql_text from v$sqltext t,v$sql_plan p Where t.hash_value=p.hash_value And p.operation='TABLE ACCESS' And p.options='FULL' Order by p.hash_value,t.piece;
3. Direct path read/write(直接路径读/写)
直接路径读通常发生在Oracle直接读取数据到PGA时,这个读取不需要经过SGA。直接路径读等待事件的3个参数分别是:file#(指绝对文件号)、first block#和block数量。
这类读取通常在以下情况被使用:
磁盘排序IO操作
并行查询从属进程
预读操作
最常见的是第一种情况。在DSS系统中,存在大量的Direct path read是很正常的,但是在OLTP系统中,通常显著的直接路径读都意味着系统应用存在问题,从而导致大量的磁盘排序读取操作。
直接路径写通常发生在Oracle直接从PGA写数据到数据文件或临时文件,这个写操作可以绕过SGA。直接路径写等待事件的3个参数分别是:file#(指绝对文件号)、first block#和block数量。
这类读取通常在以下情况被使用:
直接路径加载
并行DML操作
磁盘排序
对未缓存的“LOB”段的写入,随后会记录为direct path write(lob)等待
最常见的直接路径写,多数因为磁盘排序导致。对于这一写入等待,应该找到I/O操作最为频繁的数据文件(如果有过多的排序操作,很有可能就是临时文件),分散负载,加快其写入操作。
如果系统存在过多的磁盘排序,会导致临时表空间操作频繁,对于这种情况,可以考虑为不同用户分配不同的临时表空间,使用多个临时文件,写入不同磁盘或者裸设备,从而降低竞争提高性能。
日志文件相关等待
SQL> select name from v$event_name where name like '%log%';
NAME
----------------------------------------------------------------
log switch/archive
log file sequential read
log file single write
log file parallel write
log buffer space
log file switch (checkpoint incomplete)
log file switch (archiving needed)
log file switch (clearing log file)
switch logfile command
log file switch completion
log file sync
STREAMS capture process waiting for archive log
已选择12行。
4. Log File Switch(日志文件切换)
Log File Switch当日志文件发生切换时出现,在数据库进行日志切换时,LGWR需要关闭当前日志组,切换并打开下一个日志组,在这个切换过程中,数据库的所有DML操作都处于停顿状态,直至这个切换完成。
Log File Switch主要包含两个子事件:
1. log file switch(achiving needed),即日志切换(需要归档)
这个等待事件出现时通常是因为日志组循环写满以后,在需要覆盖先前日志时,发现日志归档尚未完成,出现该等待。由于Redo不能写出,该等待出现时,数据库将陷于停顿状态。
出现该等待,可能表示I/O存在问题、归档进程写出缓慢,也有可能是日志组设置不合理等原因导致。针对不同原因,可以考虑采用的解决方法有:
可以考虑增大日志文件和增加日志组;
移动归档文件到快速磁盘;
调整log_archive_max_processes参数等;
2. log file switch(checkpoint incomplete),即日志切换(检查电未完成)
当所有的日志组都写满之后。LGWR试图覆盖某个日志文件,如果这时数据库没有完成写出由这个日志文件所保护的脏数据时(检查点未完成),该等待事件出现。该等待出现时,数据库同样将陷于停顿状态。
该等待事件通常表示DBWR写出速度太慢或者I/O存在问题。为解决该问题,可能需要考虑增加额外的DBWR或者增加日志组或日志文件大小。
5. Log File Sync(日志文件同步)
当一个用户提交或回滚数据时,LGWR将会话期的重做由日志缓冲区写入到重做日志中,LGWR完成任务以后会通知用户进程。日志文件同步过程(Log File Sync)必须等待这一过程成功完成。对于回滚操作,该事件记录从用户发出Rollback命令道回滚完成的时间。
如果该等待过多,可能说明LGWR的写出效率低下,或者系统提交过于频繁。针对该问题,可以通过log file parallel write等待事件或User Commits、User Rollback等统计信息来观察提交或回滚次数。
可能的解决方案主要有:
1). 提高LGWR性能,尽量使用快速磁盘,不要把redo log file存放在RAID5的磁盘上;RAID5 对于频繁写入得系统会带来较大的性能损失,可以考虑使用文件系统直接输入/输出,或者使用裸设备(raw device),这样可以获得写入的性能提高。
2). 使用批量提交;
3). 适当使用NOLOGGING/UNRECOVERABLE等选项
6. Log File Single Write
该事件仅与写日志文件头块相关,通常发生在增加新的组成员和增进序列号(Log switch)时。头块写单个进行,因为头块的部分信息是文件号,每个文件不同。更新日志文件头这个操作在后台完成,一般很少出现等待,无需太多关注。
7. Log File Parallel Write
从Log Buffer写Redo记录到日志文件,主要指常规写操作(相对于Log File Sync)。如果Log Group存在多个组成员,当Flush Log Buffer时,写操作是并行的,这时候此等待事件可能出现。
尽管这个写操作并行处理,直到所有I/O 操作完成该写操作才会完成(如果你的磁盘支持异步IO或者使用IO SLAVE,那么即使只有一个redo log file member,也有可能出现此等待)。
这个参数和log file sync 时间相比较可以用来衡量log file 的写入成本。通常称为同步成本率。
8. Log Buffer Space(日志缓冲空间)
当数据库产生日志的速度比LGWR的写出速度快,或者当日志切换太慢时,就会发生这种等待。这个等待出现时,通常表明Redo log buffer过小,为解决这个问题,可以考虑增大日志文件的大小或者增加日志缓冲器的大小。
另一个可能的原因是磁盘I/O存在瓶颈,可以考虑使用写入速度更快的磁盘。在允许的条件下设置,可以考虑使用裸设备来存放日志文件,提高写入效率。在一般的系统中,最低的标准是,不要把日志文件和数据文件存放在一起,因为通常日志文件只写不读,分离存放可以获得性能提升,尽量使用RAID10而不是RAID5磁盘来存储日志文件。
9. Enqueue(队列等待)
Enqueue是一种保护共享资源的锁定机制。该锁定机制保护共享资源,以避免因并发操作而损坏数据,比如通过锁定保护一行记录,避免多个用户同时更新。Enqueue采用排队机制,即FIFO(先进先出)来控制资源的使用。
Enqueue是一组锁定事件的集合,如果数据库中这个等待事件比较显著,还需要进一步追踪是哪一个类别的锁定引发了数据库等待。
SQL> select name,wait_class from v$event_name where name like '%enq%' and rownum<11;
--这里记录很多 只去取出了前10条而已
NAME WAIT_CLASS
------------------------- ------------------------------------------------------
enq: PW - flush prewarm b Application
enq: RO - contention Application
enq: RO - fast object reu Application
enq: KO - fast object che Application
enq: MV - datafile move Administrative
enq: TM - contention Application
enq: ST - contention Configuration
enq: TX - row lock conten Application
enq: TX - allocate ITL en Configuration
enq: TX - index contentio Concurrency
已选择10行。
10. Latch Free(闩锁释放)
Latch Free通常被称为闩锁释放,这个名称常常引起误解,实际上应该在前面加上一个”等待(WAIT)”,当数据库出现这个等待时,说明有进程正在等待某个Latch被释放,也就是Waiting Latch Free。
Latch是一种低级排队(串行)机制,用于保护SGA中共享内存结构。Latch就像是一种快速的被获取和释放的内存锁,用于防止共享内存结构被多个用户同时访问。
如果latch不可用,就会记录latch释放失败(latch free miss )。有两种与闩有关的类型:
1) 立刻。
2) 可以等待。
假如一个进程试图在立刻模式下获得闩,而该闩已经被另外一个进程所持有,如果该闩不能立可用的话,那么该进程就不会为获得该闩而等待。它将继续执行另一个操作。
大多数latch问题都与以下操作相关:
没有很好的是用绑定变量(library cache latch)、重作生成问题(redo allocation latch)、缓冲存储竞争问题(cache buffers LRU chain),以及buffer cache中的存在"热点"块(cache buffers chain)。
通常我们说,如果想设计一个失败的系统,不考虑绑定变量,这一个条件就够了,对于异构性强的系统,不使用绑定变量的后果是极其严重的。
另外也有一些latch等待与bug有关,应当关注Metalink相关bug的公布及补丁的发布。当latch miss ratios大于0.5%时,就应当研究这一问题。
Oracle的latch机制是竞争,其处理类似于网络里的CSMA/CD,所有用户进程争夺latch, 对于愿意等待类型(willing-to-wait)的latch,如果一个进程在第一次尝试中没有获得latch,那么它会等待并且再尝试一次,如果经过_spin_count次争夺不能获得latch, 然后该进程转入睡眠状态,持续一段指定长度的时间,然后再次醒来,按顺序重复以前的步骤.在8i/9i中默认值是_spin_count=2000。
如果SQL语句不能调整,在8.1.6版本以上,Oracle提供了一个新的初始化参数: CURSOR_SHARING可以通过设置CURSOR_SHARING = force 在服务器端强制绑定变量。设置该参数可能会带来一定的副作用,对于Java的程序,有相关的bug,具体应用应该关注Metalink的bug公告。
11. Free Buffer-释放缓冲区
这个等待事件表明系统正在等待内存中的可用空间,这说明当前Buffer 中已经没有Free 的内存空间。如果应用设计良好,SQL 书写规范,充分绑定变量,那这种等待可能说明Buffer Cache 设置的偏小,你可能需要增大DB_BUFFER_CACHE。
Free Buffer 等待可能说明DBWR 的写出速度不够,或者磁盘存在严重的竞争,可以需要考虑增加检查点、使用更多的DBWR 进程,或者增加物理磁盘的数量,分散负载,平衡IO。
12. Buffer Busy-缓冲区忙
该等待事件表示正在等待一个以unshareable方式使用的缓冲区,或者表示当前正在被读入buffer cache。一般来说Buffer Busy Wait不应大于1%。检查缓冲等待统计部分(或V$WAITSTAT),看一下等待是否位于段头(Segment Header)。如果是,可以考虑增加自由列表(freelist,对于Oracle8i DMT)或者增加freelist groups(在很多时候这个调整是立竿见影的,在8.1.6之前,这个freelists参数不能动态修改;在8.1.6及以后版本,动态修改feelists需要设置COMPATIBLE至少为8.1.6).
如果这一等待位于undo header,可以通过增加回滚段(rollback segment)来解决缓冲区的问题。如果等待位于undo block上,我们可能需要检查相关应用,适当减少大规模的一致性读取,或者降低一致性读取(consistent read)的表中的数据密度或者增大DB_CACHE_SIZE。
如果等待处于data block,可以考虑将频繁并发访问的表或数据移到另一数据块或者进行更大范围的分布(可以增加pctfree值 ,扩大数据分布,减少竞争),以避开这个"热点"数据块,或者可以考虑增加表中的自由列表或使用本地化管理的表空间(Locally Managed Tablespaces)。
如果等待处于索引块,应该考虑重建索引、分割索引或使用反向键索引。为了防止与数据块相关的缓冲忙等待,也可以使用较小的块:在这种情况下,单个块中的记录就较少,所以这个块就不是那么"繁忙";或者可以设置更大的pctfree,使数据扩大物理分布,减少记录间的热点竞争。
在执行DML (insert/update/ delete)时,Oracle向数据块中写入信息,对于多事务并发访问的数据表,关于ITL的竞争和等待可能出现,为了减少这个等待,可以增加initrans,使用多个ITL槽。在Oracle9i 中,引入了一个新概念:ASSM(Segment Space Management Auto)。通过这个新特性Oracle 使用位图来管理空间使用。
ASSM 结合LMT 彻底改变了Oracle 的存储机制,位图freelist 能够减轻缓冲区忙等待(buffer busy wait),这个问题在Oracle9i 以前的版本里曾是一个严重的问题。
Oracle 宣称ASSM 显著地提高了DML 并发操作的性能,因为(同一个)位图的不同部分可以被同时使用,这样就消除了寻找剩余空间的串行化。根据Oracle 的测试结果,使用位图freelist 会消除所有分段头部(对资源)的争夺,还能获得超快的并发插入操作。在Oracle9i 之中,Buffer Busy wait 不再常见!
13. control file parallel write-控制文件并行写
当server 进程更新所有控制文件时,这个事件可能出现。如果等待很短,可以不用考虑。如果等待时间较长,检查存放控制文件的物理磁盘I/O 是否存在瓶颈。
多个控制文件是完全相同的拷贝,用于镜像以提高安全性。对于业务系统,多个控制文件应该存放在不同的磁盘上,一般来说三个是足够的,如果只有两个物理硬盘,那么两个控制文件也是可以接受的。在同一个磁盘上保存多个控制文件是不具备实际意义的。减少这个等待,可以考虑如下方法:
减少控制文件的个数(在确保安全的前提下)
如果系统支持,使用异步IO
转移控制文件到IO 负担轻的物理磁盘
14. control file sequential read/ control file single write 控制文件连续读/控制文件单个写对单个控制文件I/O 存在问题时,这两个事件会出现。如果等待比较明显,检查单个控制文件,看存放位置是否存在I/O 瓶颈。
15. direct path write-直接路径写该等待发生在,系统等待确认所有未完成的异步I/O 都已写入磁盘。
对于这一写入等待,我们应该找到I/O 操作最为频繁的数据文件(如果有过多的排序操作,很有可能就是临时文件),分散负载,加快其写入操作。
如果系统存在过多的磁盘排序,会导致临时表空间操作频繁,对于这种情况,可以考虑使用Local管理表空间,分成多个小文件,写入不同磁盘或者裸设备。
16. Idle Event-空闲事件
一般来说,空闲等待是指系统因为无事可做的等待,或者等待用户的请求或响应等,通常我们可以忽略这些等待事件。空闲事件可以通过stats$idle_event 表查询得到。
我们看一下系统的主要空闲等待事件,对这些事件大家应该有个大致的印象,如果你的Top 5 等待事件中,主要都是这些事件,那么一般来说你的系统是比价清闲的。
注: 整理自网络
------------------------------------------------------------------------------