Oracle Deep Internal | 第二课
2017-08-31
IT系统性能问题诊断方法论 | 第二课
2017-08-31

用户请求的数据不在Oracle的高速缓冲区的时候,服务器进程将相应的数据文件中的数据块加载到高速缓冲区。即称为常规路径(Conventional Path)I/O。常规路径I/O分为多块(Multi Block)I/O和单块(Single Block)I/O。多块I/O可一次读取多个连续的块的I/O方式,单块I/O每次只能读取一个块的I/O方式。
Oracle在执行全表扫描(Full Table Scan。FTS)或快速全索引扫描(Index Fast Full Scan)的时候,为了保证性能尽可能执行将多个连续的块的一次性读取的多块I/O。每次执行多块I/O的时候,直等到物理I/O的结束为止,发生db file scattered read等待事件。
多块I/O是参照DB_FILE_MULTIBLOCKREAD_COUNT参数所指定的值执行。这个值的最大值随OS不同而最大值也不尽相同,可通过以下方法查看最大值。

 

Oracle在执行FTS的时候,有时也进行单块I/O。这时候就算FTS,也会发生db file sequential read的等待。在FTS中,使用单块I/O或者读取比MBRC小的块的情况如下。

  • 达到区(Extent)界限的时候:假设一个区有9个块,而一次多块I/O可读取8个块,那么剩下的一个块由单块I/O来读取。如果剩下的块是2个块,那么执行多块I/O来读取且只读2个块。
  • 扫描过程中如发现缓存(Cache)的块时:假设在读取8个块的时候,其中第三个块已经缓存,那么Oracle将前面的两个块以多块IO读取,而对第三个块则执行一次逻辑(Logical)IO,再将剩下的5个块以多块IO来读取。如果这种情况经常发生,可引发不必要的IO,进而成为降低FTS速度原因。
  • 有链接行(Chained Row)的情况: 在执行FTS的过程中,如果遇到链接行(Chained Row),则oracle为了读取剩下的行将发生额外的IO,这时将执行单块IO。有必要明确了解链接行(Chained Row)和迁移行(Migrated Row)的不同之处。链接行在行的大小比块大的时候才会发生。除了用更大的块或者减少PCTFREE的方法,没有根本上撤销链接行的方式。迁移行(Migrated Row)初始是进入了一个块,但后来随着行的大小增加导致现有空间不够时发生。这时实际行是迁移到另外的块中,在原来的行中记录了表示迁移去的块和行位置的ROWID。迁移行特别是在通过索引扫描表的时候,对性能有很大的影响。因为为了读一行记录,需要读两个块。所以需要考虑一些迁移行对FTS的影响。FTS的工作原理是对最高水位线(HWM)以下的所有块从头开始读取。Oracle在执行FTS过程中,如果遇到迁移行不会额外增加单块I/O,而是继续执行读取工作。因为知道反正在执行扫描过程中会重新读取的。如果HWM的位置相同,不管有没有迁移行对FTS自身的性能是没有影响的。当然发生迁移时后,会额外增加区,并且HWM移动得越远,必然会对FTS的性能产生影响。要消除迁移行的时候要注意这一点。

Parameter & Wait Time

Wait Parameters

db file scattered read 等待事件下的参数如下。

  • P1 : File#
  • P2 : Starting Block#
  • P3 : 块数

Wait Time

作为I/O相关等待事件,不会发生超时,会话是对指定个数的块进行I/O结束为止等待。


Check Point & Solution

OracleI/O层的解决方案

将相应数据加载到SGA是I/O一般任务。所以 db file scattered read事件跟db file sequential read事件是在Oracle中最常见的等待事件。因为要想在数据文件中读取的块,只能进行多块IO或单块IO。人们之所以忌讳db file scattered read等待是因为其跟物理I/O有关联,并且跟FTS一同出现。 最终这个问题是跟FTS的好坏的争用有关联,答案或‘好’或‘不好’。所有的工作只有在适当使用时才是好的,如若不然就是坏的。假设将大范围的索引扫描将会用尽高速缓冲区造成致命的性能问题。

以oralce的I/O层作为基准讨论一下db file scattered read的等待问题吧。

应用层:

提取发生db file scattered read等待的主要SQL语句。如果使用了不必要的FTS或执行了快速全索引扫描的话,通过修改SQL语句或创建更为合理的索引来解决问题。在读取大范围的数据的时候,FTS往往是更有利的。不是勉强地创建的索引,而是根据考虑SQL语句的特征后判断是FTS有利还是索引域扫描(Index Range Scan)有利。

Oracle内存层:

如果高速缓冲区过小,将重复地需要进行物理I/O,相应地db file scattered read等待也会增加。这时,free buffer waits等待事件一起出现的概率很高。对于FTS的db file scattered read等待的严重性,不只是造成读取工作的性能下降,还会因为高速缓冲区的效率降低而影响会话工作。在这个观点下,有效处理FTS的方法中一个是使用多个缓冲池(Buffer Pool)。反正在读完一次之后不再重复使用的数据没有必要保存在高速缓冲区给其他用户的工作带来影响。虽然多个缓冲池是有效的管理高速缓冲区的强有力地手段,但是不幸的没有被很好地使用。通过实际测试来验证一下,这个功能到底对性能有多大的影响吧。测试方案如下。

  • 创建大小为16M左右的5个表READ_TEST1~READ_TEST5。
  • 在Case1中赋予的DB_CACHE_SIZE的值为32M,再把5个表加载到Default缓冲池。
  • 在Case2中赋予的DB_CACHE_SIZE的值为16M,,DB_RECYCLE_CAHCE_SIZE值为16M,把READ_TEST1表加载到Default缓冲区,READ_TEST2~READ_TEST5则加载到RECYCLE缓冲池。
  • 确认一下Case1和Case2中db file scattered read等待怎样发生。

– 测试的目的是5个会话同时分别对表read_test1~read_test5执行FTS的时候(即,会话1对表read_test1扫描,会话2对表read_test1扫描,会话3对表read_test1扫描……),保证read_test1的扫描速度。其中表read_test1是经常被访问的表.

Case1 : 表read_test1 ~ read_test5都是在使用默认缓冲池的时候,将呈现出较高的db file scattered read等待时间。

Case2: 因为read_test2 ~ read_test5表使用Recycle缓冲池,对默认(Default)缓冲池的争用会消失。对于使用默认缓冲池的表read_test1执行FTS的会话1的db file scattered read等待时间相比其他会话短。有趣的是,不只是read_test1表,而且read_test2 ~ read_test5表的db file scattered read等待时间也随之缩短。这可以解释成为是因为减少了对高速缓冲区的争用,进而其效果同时反映在两个缓冲池上。

请记住:如上所示,有效使用缓冲池时会收到相当的性能改善的效果。多重缓冲池可以再三个方面改善高速缓冲区的性能。第一、将经常被访问的物体驻留在内存内,将物理I/O最小化;第二、对挥发性的数据快速的在内存中重复使用,将内存的浪费最小化;第三、在每个缓冲区上使用不同的cache buffers lru chain锁存器,从而减少锁存器的争用。

有效执行FTS的另一个种方法是提高DB_FILE_MULTIBLOCK_READ_COUNT的参数值。这个参数值决定执行多块IO的时候读取的块数。这个值越高,FTS的速度也会改善,相应地db file scattered read等待也会减少。将该值在系统整体层面调整是不可取的,最好使用‘alter session set …’命令,设定为在执行特定SQL语句时候才会将其提高。该值越高对于FTS的费用的计算会降低,导致会变更SQL的执行。

使用大的块也是改善FTS性能的方法。大的块在如下两个方面改善FTS的性能。第一、因一个块中包含的行数(记录)的增加,在组成相同大小的表的时候使用较少的块;第二、块的大小越大,行链接(Row chaining)或行迁移(Row migration)的发生概率减少。随之引发的额外多块IO的次数也减少。大部分的OLTP系统上,都只使用标准的大小(8KB)。但是需要经常访问大量的数据在DSS系统中,使用更大的块达到性能改善的效果。

Oracle段层

有必要检查一下是否可以通过进行适当的分区(Partitioning)减少FTS的范围。假设为了100万行数据中的10万行数据执行FTS,将10万行相对应的的范围分区,就可以将FTS的范围减少1/10。

 

OS/设备层

如果优化SQL或高速缓冲区也解决不了问题,需要怀疑I/O 系统自身的性能问题了。通过比较db file scattered read的等待次数和等待时间,如果平均等待时间长,则原因可能是缓慢的I/O系统。如前面所说I/O系统的性能问题会在很多种情况下都会发生。需要检查各种各样的因素。

利用V$FILESTAT视图可以获得各数据类文件的多块IO和单块IO的活动情况信息。

如果特定文件的平均执行时间过长,可通过提升文件所在的I/O系统的性能来改善性能。 不存在对于多块IO的合理的平均等待时间的绝对数值。只是一般情况下需要保持10微秒(ms)左右的平均等待时间。


Event Tip

Physical I/O分类

Physical I/O分为传统路径(Conventional Path)I/O和直接路径(Direct Path)I/O。传统路径I/O是通过一般所知的高速缓冲区的缓存上加载读取的块。直接路径I/O是不将数据文件的块加载到高速缓冲区,而是加载到PGA。如果发生直接路径I/O,在发生I/O工作之前发生检查点(Check Point),将脏缓冲区的数据写入到文件上,把数据文件和高速缓冲区的内容进行同步之后进行直接路径I/O。

 

Index Full Scan VS Index Fast Full Scan

在索引扫描中可能发生的性能问题大部分是因为在大范围的索引的扫描中引起大量的I/O发生量的增多造成的。那么,在发生过大范围的索引扫描的时候应该怎么办呢?

必须要发生大量的索引扫描,且不需要排序(Sorting)时候,可利用全索引快速扫描(Index fast full scan),引导其不进行单块I/O而进行多块I/O在性能方面更加有利。乍一看将全索引扫描(Index full scan)和全表扫描混淆产生误会,全索引扫描是发生单块I/O,会成为增加I/O发生的原因。这时必须引导到其执行进行多块I/O的全索引快速扫描。

控制全索引快速扫描的参数是“_FAST_FULL_SCAN_ENABLED=TRUE”,而默认值为TRUE.

引导全索引快速扫描提示是/*+ index_ffs(table_alias index_name) */。

 

DB_FILE_MULTIBLOCK_READ_COUNT (MBRC) 配置

正如前面提到的,高数值的MBRC会影响优化器首选全表扫描。适当的数值随应用程序(DSS或OLTP)不同而不同。高数值的MBRC会使全表扫描的执行速度更快,对进行批处理有利。如果数据库服务器上,同时进行服务器批处理和OLTP处理,需要调增恰当的数值。默认设置8是相对较小的。

如果全表扫描是最好的方法,希望以系统中提供的最大值来扫面对象。为什么用小的值就是浪费时间呢? 在确定最大是多少之后,再对进行全表扫描的进程实施相应值。.

MBRC是有限制的。会受到sstiomaxDB_BLOCK_SIZEDB_BLOCK_BUFFERS等几个因素左右。sstiomax是Oracle的内部阈值,这是在读、写、执行时,限制一次性可传送I/O的数据量。此值是在Oracle代码内部早已设定的,而且随Oracle的版本不同而不同。Oracle初期版本是128K,但是从Oracle8开始是1M。DB_BLOCK_SIZE和MBRC的乘值是不能超过sstiomax。还有,MBRC要小于DB_BLOCK_BUFFERS / 4的值。而且,MBRC受限于Solaris的maxphys和文件系统的maxcontig等。如果说这样太难了,也有根据环境而设定的简单的方法。就是将MBRC的值跟下面的例子一样设定为一个很大的值。那么Oracle将用系统中的最大值来处理。之后运行执行全表扫描的SQL语句后查询V$SESSION_WAIT 即可。db file scattered read的等待事件P3参数会变更为现在系统的最大值。另外一个方法是设定10046追踪事件。这个值与其在数据库级别中设定,不如设定在要改善全表扫描速度的会话上。

 

为什么Physical I/O费用贵呢?

许多DBA都听过磁盘I/O费用昂贵,他们所受的教育是思考集中在物理磁盘和I/O子系统中。当然,存储层是最为缓慢的组件。但这不是缓慢的全部理由。剩下的部分是将数据块加载到SGA时内部发生的事情。

将数据块加载到SGA时发生许多事情。简单来说,前台进程首先会检索空闲缓存列。若达到maximum scan limit为止没有找到空闲缓存时,前台进程将请求DBWR进程创建空闲缓存。一旦找到空闲缓存,从空闲列表链(free list chain)中解除响应块后,将相应块移动到LRU列的上端或LRU列的中间位置 (将移动LRU列的中间位置称为midpoint insertion,是从Oracle 8i开始应用的方式)。此后响应缓存头的指针将适当调整。至少有两个指针集,每次变更时都要获取锁存器。块的头结构也要被初始化后修改。分配缓存或将块加载到高速缓存,或加载结束为止,为了防止其他进程访问相应块,要把块头的特定位(bit)进行初始化修正。

结论性地解决db file sequential read db file scattered read等待的最佳方法就是减少内存I/O和磁盘I/O。这是需要通过应用和SQL调优才有可能。现在可以知道磁盘I/O多么昂贵,内存I/O又多么昂贵。

Leave a Reply

Be the First to Comment!

Notify of
avatar
wpDiscuz