在V$SESSION_WAIT这个视图里面,这个等待事件有三个参数P1、P2、P3,其中P1代表Oracle要读取的文件的绝对文件号,P2代表Oracle从这个文件中开始读取的BLOCK号,P3代表Oracle从这个文件开始读取的BLOCK号后读取的BLOCK数量。
SELECT *
FROM v$event_name
WHERE NAME IN (‘db file sequential read‘, ‘db file scattered read‘);
从V$EVENT_NAME视图可以看到,该等待事件有3个参数:
File#: 要读取的数据块所在数据文件的文件号。
Block#: 要读取的起始数据块号。
Blocks:需要读取的数据块数目。
起始数据块号加上数据块的数量,这意味着Oracle session正在等待多块连续读操作的完成。
这个等待事件在实际生产库中经常可以看到,这是一个用户操作引起的等待事件,当用户发出每次I/O需要读取多个数据块这样的SQL 操作时或者说当Oracle从磁盘上读取多个BLOCK到不连续的高速缓存区的缓存中,会产生这个等待事件,这个事件表明用户进程正在读数据到Buffer Cache中,等待直到物理I/O调用返回。最常见的两种情况是全表扫描(FTS: Full Table Scan)和索引快速全扫描(IFFS: index fast full scan)。根据经验,通常大量的db file scattered read等待可能意味着应用问题或者索引缺失。Oracle一次能够读取的最多的BLOCK数量是由初始化参数DB_FILE_MULTIBLOCK_READ_COUNT来决定。
这个名称中的scattered(发散),可能会导致很多人认为它是以scattered 的方式来读取数据块的,其实恰恰相反,当发生这种等待事件时,SQL的操作都是顺序地读取数据块的,比如FTS或者IFFS方式(如果忽略需要读取的数据块已经存在内存中的情况)。这里的scattered指的是读取的数据块在内存中的存放方式,他们被读取到内存中后,是以分散的方式存在在内存中,而不是连续的。
在生产环境之中,db file scattered read这个等待事件可能更为常见。DB File Scattered Read发出离散读,将存储上连续的数据块离散的读入到多个不连续的内存位置。Scattered Read通常是多块读,在Full Table Scan或Fast Full Scan等访问方式下使用。Scattered Read代表Full Scan,当执行Full Scan读取数据到Buffer Cache时,通常连续的数据在内存中的存储位置并不连续,所以这个等待被命名为Scattered Read(离散读)。每次多块读读取的数据块数量受初始化参数DB_FILE_MULTIBLOCK_READ_COUNT限制。图9-17简要说明了Scattered Read的数据读取方式。
完成对等待事件的分类之后,Oracle 10g的ADDM可以很容易地通过故障树分析定位到问题所在,帮助用户快速发现数据库的瓶颈及瓶颈的根源,这就是Oracle的ADDM专家系统的设计思想。通过图9-18可以直观而清晰地看到这个等待模型和ADDM结合实现的Oracle专家诊断系统。
这种情况通常显示与全表扫描相关的等待。当数据库进行全表扫时,基于性能的考虑,数据会分散(scattered)读入Buffer Cache。如果这个等待事件比较显著,可能说明对于某些全表扫描的表,没有创建索引或者没有创建合适的索引,我们可能需要检查这些数据表已确定是否进行了正确的设置。然而这个等待事件不一定意味着性能低下,在某些条件下Oracle 会主动使用全表扫描来替换索引扫描以提高性能,这和访问的数据量有关,在CBO 下Oracle 会进行更为智能的选择,在RBO 下Oracle 更倾向于使用索引。因为全表扫描被置于LRU(Least Recently Used,最近最少适用)列表的冷端(cold end),对于频繁访问的较小的数据表,可以选择把他们Cache 到内存中,以避免反复读取。
在实际环境的诊断过程中,可以通过v$session_wait视图发现session的等待,再结合其他视图找到存在问题的SQL等根本原因,从而从根本上解决问题。在11g也可以直接通过v$session视图来查询等待事件。当这个等待事件比较显著时,也可结合v$session_longops动态性能视图来进行诊断,该视图记录了长时间(运行时间超过6秒的)运行的事务。
如果这个等待事件在整个等待时间中占了比较大的比重,可以如下的几种方法来调整Oracle数据库:
方法一:找出执行全表扫描(FTS: Full Table Scan)和索引快速全扫描(IFFS: index fast full scan)扫描的SQL语句,判断这些扫描是否是必要的,是否导致了比较差的执行计划,如果是,则需要调整这些SQL语句,可以结合v$session_longops 动态性能视图来进行诊断,该视图中记录了长时间(运行时间超过6 秒的)运行的事物,可能很多是全表扫描操作。
从Oracle9i开始提供了一个视图V$SQL_PLAN用于记录当前系统Library Cache中SQL语句的执行计划,可以通过这个视图找到存在问题的SQL语句,即可以很快的帮助找到那些全表扫描或者Fast Full Index扫描的SQL语句,这个视图会自动忽略掉关于数据字典的SQL语句。
查找全表扫描的SQL语句可以使用如下语句:
通过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;
获得全表扫描的对象
SELECT DISTINCT object_name,
object_owner
FROM v$sql_plan p
WHERE p.operation = ‘TABLE ACCESS‘
AND p.options = ‘FULL‘
AND object_owner = ‘SYS‘;
查找Fast Full Index扫描的SQL语句可以使用如下语句:
SELECT sql_text
FROM v$sqltext t,
v$sql_plan p
WHERE t.hash_value = p.hash_value
AND p.operation = ‘INDEX‘
AND p.options = ‘FULL SCAN‘
ORDER BY p.hash_value, t.piece;
获得全索引扫描的对象
SELECT DISTINCT object_name,
object_owner
FROM v$sql_plan p
WHERE p.operation = ‘INDEX‘
AND p.options = ‘FULL SCAN‘
AND object_owner = ‘SYS‘;
如果是Oracle8i的数据库,可以从V$SESSION_EVENT视图中找到关于这个等待事件的进程sid,然后根据sid来跟踪相应的会话的SQL。
select sid,event from v$session_event where event=‘db file sequential read‘
或者可以查看物理读取最多的SQL语句的执行计划,看是否里面包含了全表扫描和Fast Full Index扫描。通过如下语句来查找物理读取最多的SQL语句:
select sql_text from (
select * from v$sqlarea
order by disk_reads)
where rownum<=10;
方法二:有时候在执行计划很好情况下也会出现多BLOCK扫描的情况,这时可以通过调整Oracle数据库的多BLOCK的I/O,设置一个合理的Oracle初始化参数DB_FILE_MULTIBLOCK_READ_COUNT,尽量使得满足以下的公式:
DB_BLOCK_SIZE x DB_FILE_MULTIBLOCK_READ_COUNT = max_io_size of system
DB_FILE_MULTIBLOCK_READ_COUNT是指在全表扫描中一次能够读取的最多的BLOCK数量,这个值受操作系统每次能够读写最大的I/O限制,如果设置的值按照上面的公式计算超过了操作系统每次的最大读写能力,则会默认为max_io_size/db_block_size。例如DB_FILE_MULTIBLOCK_READ_COUNT设置为32,DB_BLOCK_SIZE为8K,这样每次全表扫描的时候能读取256K的表数据,从而大大的提高了整体查询的性能。设置这个参数也不是越大越好的,设置这个参数之前应该要先了解应用的类型,如果是OLTP类型的应用,一般来说全表扫描较少,这个时候设定比较大的DB_FILE_MULTIBLOCK_READ_COUNT反而会降低Oracle数据库的性能,因此CBO在某些情况下会因为多BLOCK读取导致COST比较低从而错误的选用全表扫描。
方法三: 通过对表和索引使用分区、将缓存区的LRU末端的全表扫描和IFFS扫描的的BLOCK放入到KEEP缓存池中等方法调整这个等待事件。