等待事件的源起
等待事件的概念大概是从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)等待事件。空闲等待事件指ORACLE正等待某种工作,在诊断和优化数据库的时候,不用过多注意这部分事件。非空闲等待事件专门针对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
WAIT_CLASS# WAIT_CLASS_ID WAIT_CLASS count
----------- ------------- ------------------------------ ----------
11 3871361733 Cluster 47
12 rows selected.
几个视图的总结:
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语句。
重要等待事件
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
2 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可以选择正确的执行计划,避免因为统计信息陈旧而导致的执行错误等。
Db file scattered read(数据文件离散读取)
SQL> select * from v$event_name where name='db file scattered read';
EVENT# NAME
---------- ----------------------------------------------------------------
PARAMETER1
----------------------------------------------------------------
PARAMETER2
----------------------------------------------------------------
PARAMETER3
----------------------------------------------------------------
188 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=’SCOTT’;
获得全索引扫描的对象
Select distinct object_name,object_owner from v$sql_plan p
Where p.operation=’INDEX’ and p.options=’FULL SCAN’ and object_owner=’SCOTT’;
通过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;
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行。
Log File Switch(日志文件切换)
Log File Switch当日志文件发生切换时出现,在数据库进行日志切换时,LGWR需要关闭当前日志组,切换并打开下一个日志组,在这个切换过程中,数据库的所有DML操作都处于停顿状态,直至这个切换完成。
Log File Switch主要包含两个子事件:
log file switch(achiving needed),即日志切换(需要归档)
这个等待事件出现时通常是因为日志组循环写满以后,在需要覆盖先前日志时,发现日志归档尚未完成,出现该等待。由于Redo不能写出,该等待出现时,数据库将陷于停顿状态。
出现该等待,可能表示I/O存在问题、归档进程写出缓慢,也有可能是日志组设置不合理等原因导致。针对不同原因,可以考虑采用的解决方法有:
可以考虑增大日志文件和增加日志组;
移动归档文件到快速磁盘;
调整log_archive_max_processes参数等;
log file switch(checkpoint incomplete),即日志切换(检查电未完成)
当所有的日志组都写满之后。LGWR试图覆盖某个日志文件,如果这时数据库没有完成写出由这个日志文件所保护的脏数据时(检查点未完成),该等待事件出现。该等待出现时,数据库同样将陷于停顿状态。
该等待事件通常表示DBWR写出速度太慢或者I/O存在问题。为解决该问题,可能需要考虑增加额外的DBWR或者增加日志组或日志文件大小。
Log File Sync(日志文件同步)
当一个用户提交或回滚数据时,LGWR将会话期的重做由日志缓冲区写入到重做日志中,LGWR完成任务以后会通知用户进程。日志文件同步过程(Log File Sync)必须等待这一过程成功完成。对于回滚操作,该事件记录从用户发出Rollback命令道回滚完成的时间。
如果该等待过多,可能说明LGWR的写出效率低下,或者系统提交过于频繁。针对该问题,可以通过log file parallel write等待事件或User Commits、User Rollback等统计信息来观察提交或回滚次数。
可能的解决方案主要有:
提高LGWR性能,尽量使用快速磁盘,不要把redo log file存放在RAID5的磁盘上;
使用批量提交;
适当使用NOLOGGING/UNRECOVERABLE等选项
Log File Single Write
该事件仅与写日志文件头块相关,通常发生在增加新的组成员和增进序列号(Log switch)时。头块写单个进行,因为头块的部分信息是文件号,每个文件不同。
Log File Parallel Write
从Log Buffer写Redo记录到日志文件,主要指常规写操作(相对于Log File Sync)。如果Log Group存在多个组成员,当Flush Log Buffer时,写操作是并行的,这时候此等待事件可能出现。
Log Buffer Space(日志缓冲空间)
当数据库产生日志的速度比LGWR的写出速度快,或者当日志切换太慢时,就会发生这种等待。这个等待出现时,通常表明Redo log buffer过小,为解决这个问题,可以考虑增大日志文件的大小或者增加日志缓冲器的大小。
另一个可能的原因是磁盘I/O存在瓶颈,可以考虑使用写入速度更快的磁盘。在允许的条件下设置,可以考虑使用裸设备来存放日志文件,提高写入效率。在一般的系统中,最低的标准是,不要把日志文件和数据文件存放在一起,因为通常日志文件只写不读,分离存放可以获得性能提升,尽量使用RAID10而不是RAID5磁盘来存储日志文件。
Enqueue(队列等待)
Enqueue是一种保护共享资源的锁定机制。该锁定机制保护共享资源,以避免因并发操作而损坏数据,比如通过锁定保护一行记录,避免多个用户同时更新。Enqueue采用排队机制,即FIFO(先进先出)来控制资源的使用。
Enqueue是一组锁定事件的集合,如果数据库中这个等待事件比较显著,还需要进一步追踪是哪一个类别的锁定引发了数据库等待。
SQL> select name,wait_class
2 from v$event_name where name like '%enq%'
3 and rownum<11; --这里记录很多 只去取出了前10条而已
NAME WAIT_CLASS
------------------------------ ------------------------------
enq: PW - flush prewarm buffer Application
s
enq: RO - contention Application
enq: RO - fast object reuse Application
enq: KO - fast object checkpoi Application
nt
enq: TM - contention Application
enq: ST - contention Configuration
enq: HW - contention Configuration
NAME WAIT_CLASS
------------------------------ ------------------------------
enq: SS - contention Configuration
enq: TX - row lock contention Application
enq: TX - allocate ITL entry Configuration
10 rows selected.
Latch Free(闩锁释放)
Latch Free通常被称为闩锁释放,这个名称常常引起误解,实际上应该在前面加上一个”等待(WAIT)”,当数据库出现这个等待时,说明有进程正在等待某个Latch被释放,也就是Waiting Latch Free。
Latch是一种低级排队(串行)机制,用于保护SGA中共享内存结构。Latch就像是一种快速的被获取和释放的内存锁,用于防止共享内存结构被多个用户同时访问。