buffer cache深度分析之buffer cache的优化

1 buffer cache的设置优化

   buffer cache的设置随着oracle版本的升级而不断变化。8i下使用db_block_buffers来设置,该参数表示buffer cache中所可以包含的内存数据块的个数;9i之后使用db_cache_size来设置,该参数表示buffer cache的总共的容量,能够用字节、K、M为单位来进行设置。而到了10g之后则更加简单,甚至能够不用去单独设置buffer cache的大小。由于10g引入了ASMM(Automatic Shared Memory Management)这样一个能够进行自我调整的组件,该组件能够自动调整shared pool size、db cache size等SGA中的组件。只须要设置sga_target参数,则其余组件就可以根据系统的负载和历史信息自动的调整各个部分的大小。要启动ASMM,只须要设置statistics_level为typical或all。

   oracle8.0之前只能设置一种buffer cache,而从8.0之后,oracle提供了三种类型的buffer cache,分别是default、keep、recyle。keep和recycle是可选的,default必须存在。8i之前使用db_block_buffer设置default、buffer_pool_keep设置keep、buffer_pool_recycle设置recyle。而8i之后使用db_cache_size设置default、db_keep_cache_size设置keep、db_recycle_cache_size设置recycle。10g不能自动设置db_keep_cache_size和db_recycle_cache_size,必须手工设置。
同时,8i之前,这三种buffer cache是独立指定的,互不制约。而8i之后,这三种buffer cache是有相互制约关系的。若是指定了keep和recycle的buffer cache,则default类型的buffer cache的大小就是db_cache_size - buffer_pool_keep - buffer_pool_recycle。

    一般将常常访问的对象放入keep类型的buffer cache里,而将不常访问的大表放入recycle类型的buffer cache里。其余没有指定buffer cache类型的对象都将进入default类型的buffer cache里。为对象指定buffer cache类型的方法以下:
   
   
   
   
SQL > create table test (n number ) storage (buffer_pool keep); SQL > alter table test storage (buffer_pool recycle);
  若是没有指定buffer_pool短语,则表示该对象进入default类型的buffer cache。

  这里要说明的是,从名字上看,很容易让人误觉得这三种buffer cache提供了三种不一样的管理内存数据块的机制。但事实上,它们之间在管理和内部机制上没有任何的区别。它们仅仅是为DBA们提供了一个选择,就是可以将数据库对象分红“很是热的”、“比较热的”和“不热的”这三种类型。由于数据库中总会存在一些“很是热”的对象,它们频繁的被访问。而若是某个时候系统偶尔作了一次大表的全表扫描,就有可能将这些对象清除出内存。为了防止这种状况的发生,咱们能够设置keep类型的buffer cache,并将这种对象都移入keep buffer cache中。一样的,数据库中也总会有一些很大的表,可能天天为了生成一张报表,而只须要访问一次就能够了。但有可能就是这么一次访问,就将大部分的内存数据块清除出了buffer cache。为了不这种状况的发生,能够设置recycle类型的buffer cache,并将这种偶尔访问的大表移入recycle buffer cache。

  毫无疑问,若是你要设置这三种类型的buffer cache,你须要本身研究并等于你的数据库中的对象进行分类,并计算这些对象的大小,从而才可以正确的把它们放入不一样的buffer cache。可是,无论怎么说,设置这三种类型的buffer cache只能算是最低层次的优化,也就是说在你没有任何办法的状况下,能够考虑设置他们。可是若是你可以优化某条buffer gets很是高SQL使其buffer gets下降50%的话,就已经比设置多个buffer cache要好不少了。

    9i之后还提供了能够设置多种数据块尺寸(二、四、八、16 或 32k)的buffer cache,以便存放不一样数据块尺寸的表空间中的对象。使用初始化参数:db_Nk_cache_size来指定不一样数据块尺寸的buffer cache,这里的N就是二、四、八、16 或 32。建立数据库时,使用初始化参数:db_block_size所指定缺省的数据块尺寸用于system表空间。而后能够指定最多4个不一样数据块尺寸的表空间,每种数据块尺寸的表空间必须对应一种不一样尺寸的buffer cache,不然不能建立不一样数据块尺寸的表空间。
   
   
   
   
SQL > create tablespace tbs_test_16k 2 datafile ' C:\oracle\oradata\ora92\tbs_test_16k.dbf ' size 10M 3 blocksize 16k; create tablespace tbs_test_16k * ERROR 位于第 1 行: ORA - 29339 : 表空间块大小 16384 与配置的块大小不匹配 SQL > show parameter db_16k_cache_size NAME TYPE VALUE -- ---------------------------------- ----------- ------------------------------ db_16k_cache_size big integer 0
  咱们能够看到,因为16k数据块所对应的buffer cache没有指定,因此建立16k数据块的表空间会失
败。因而咱们先设置db_16k_cache_size,而后再试着建立16k数据块的表空间。
   
   
   
   
SQL > alter system set db_16k_cache_size = 10M; 系统已更改。 SQL > create tablespace tbs_test_16k 2 datafile ' C:\oracle\oradata\ora92\tbs_test_16k.dbf ' size 10M 3 blocksize 16k; 表空间已建立。
  不一样尺寸数据块的buffer cache的管理和内部机制与缺省数据块的buffer cache没有任何的分别。它最大的好处是,当使用可传输的表空间从其余数据库中将不一样于当前缺省数据块尺寸的表空间传输过来的时候,能够不作不少处理的直接导入到当前数据库,只须要设置对应的数据块尺寸的buffer cache便可。同时,它对于调优OLTP和OLAP混合的数据库也有必定的用处。OLTP环境下,倾向于使用较小的数据块,而OLAP环境下,因为基本都是执行全表扫描,所以倾向于使用较大的数据块。这时,能够将OLAP的表转移到使用大数据块(好比32k)的表空间里去。而将OLTP的表放在中等大小的数据块(好比8k)的表空间里。
对于应该设置buffer cache为多大,oracle从9i开始经过设置初始化参数:db_cache_advice,从而提供了能够参照的建议值。oracle会监控default类型、keep类型和recycle类型的buffer cache的使用,以及其余五种不一样数据库尺寸(二、四、八、16 或 32k)的buffer cache的使用。在典型负荷的时候,启用该参数,从而收集数据帮助用户肯定最佳的db_cache_size的大小。该参数有三个值:
   1) off:不收集数据。
   2) on:开始分配内存收集数据,有可能引起CPU和内存的负担,可能引发4031错。
   3) ready:不收集数据,可是收集数据的内存已经预先分配好了。经过把该参数值从off设置为ready,而后再设置为on,就能够避免出现4031错。

    oracle会根据当前所监控到的物理读的速率,从而估算出在不一样大小尺寸的buffer cache下,所产生的可能的物理读的数量。oracle会将这些收集到的信息放入视图:v$db_cache_advice中。每种类型的buffer cache都会有相应的若干条记录来表示所建议的buffer cache的大小。好比下面,咱们显示对于缺省类型的、缺省数据块尺寸的buffer cache的建议大小应该是多少。
   
   
   
   
SQL > SELECT size_for_estimate, buffers_for_estimate, 2 estd_physical_read_factor,estd_physical_reads 3 FROM v$db_cache_advice 4 WHERE NAME = ' DEFAULT ' 5 AND block_size = ( SELECT VALUE 6 FROM v$parameter 7 WHERE NAME = ' db_block_size ' ) 8 / SIZE_FOR_ESTIMATE BUFFERS_FOR_ESTIMATE ESTD_PHYSICAL_READ_FACTOR ESTD_PHYSICAL_READS -- --------------- -------------------- ------------------------- ------------------- 4 500 1.3869 40154 8 1000 1.3848 40093 12 1500 1.1861 34339 16 2000 1.1397 32996 20 2500 1 28952 24 3000 1 28952 28 3500 1 28952 32 4000 1 28952 36 4500 0.8671 25104 40 5000 0.8671 25104 44 5500 0.8671 25104 48 6000 0.7422 21488 52 6500 0.7422 21488 56 7000 0.7422 21488 60 7500 0.554 16040 64 8000 0.554 16040 68 8500 0.554 16040 72 9000 0.554 16040 76 9500 0.554 16040 80 10000 0.554 16040
    这里的字段estd_physical_read_factor表示在相应的buffer cache尺寸(由字段size_for_estimate表示)
 下,估计从硬盘里读取数据的次数除以在内存里读取数据的次数。若是没有发生物理读则该比值为空。在
 内存足够的前提下,这个比值应该是越低越好的。从上面的输出中,咱们能够看到,若是将buffer cache
设置为60M,能够得到较好的性能,物理读也将会有一个显著的降低。可是设置为大于60M的话(好比
64M或68M),则不会下降物理读,反而浪费内存空间。因此从上面的查询结果中,咱们能够知道,设置
为60M是比较合适的。
2 buffer cache的统计信息

   为了对buffer cache进行性能的诊断,oracle提供了不少有关buffer cache的统计信息。这些统计信息大体能够分红三类:1)有关用户发出的对内存数据块的请求相关的统计信息;2)有关DBWR后台进程对内存数据块处理相关的统计信息;3)RAC相关的统计信息。

      咱们在诊断buffer cache时,不须要关注全部的统计信息。这里主要介绍几个重要的统计信息,其余的统计信息均可以到《 Oracle9i Database Reference: Appendix C》中找到。以下所示:

   
   
   
   
SQL > SELECT name, value FROM v$sysstat WHERE name in ( 2 ' session logical reads ' , 3 ' physical reads ' , 4 ' physical reads direct ' , 5 ' physical reads direct (lob) ' , 6 ' consistent gets ' , 7 ' db block gets ' , 8 ' free buffer inspected ' ) 9 / NAME VALUE -- -------------------------------------------------------------- ---------- session logical reads 73797 db block gets 498 consistent gets 73299 physical reads 29017 free buffer inspected 0 physical reads direct 40
  这里作些简单的解释。
     1) session logical reads:全部的逻辑读的数据块的数量。注意,其中包括先从硬盘上读数据块到内存里,再从内存里读数据块。
    2) consistent gets:在一致性(consistent read)读模式下读取的内存里的数据块数量。包括从rollback segment里读取的数据块数量以及从data block buffer里读取的数据块数量。主要是经过select产生的。Update/delete也能产生不多量的此类数据块。注意:若是oracle的运行时间过长,因为oracle的bug致使consistent gets大大超过实际的数量。所以建议使用‘no work - consistent read gets’, ‘cleanouts only - consistent read gets’,‘rollbacks only - consistent read gets’, ‘cleanouts and rollbacks - consistent read gets’之和来代替consistent gets的值。
    3) db block gets:在当前(current)模式下读取的内存里的数据块的数量。不是读取过去某个时点的数据块,而必须是当前最新的数据块。主要是经过update/delete/insert来产生的,由于DML须要当前最新的数据块才能对之进行改变。在字典管理表空间下,一些得到当前可用扩展空间的select语句也会产生此类数据块,由于必须获得当前最新的空间使用信息才能扩展。逻辑上,session logical reads = consistent gets + db block gets。
    4) physical reads:从硬盘里读取的数据块的数量。注意,这个数量大于实际从硬盘里读取的数量,由于这部分block也包括了从操做系统缓存里读取的数据块数量。
    5) physical reads direct:有些数据块不会先从硬盘读入内存再从内存读入PGA再传给用户,而是绕过SGA直接从硬盘读入PGA。好比并行查询以及从临时表空间读取数据。这部分数据块因为不缓存使得hit ratio不会被提升。
    6) physical reads direct (lob):与physical reads direct同样。
    7) free buffer inspected:这个值表示为了找到可用数据块而跳过的数据块的数量。这些被跳过的数据块就是脏的或被锁定的数据块。明显,这个值若是持续增加或很高,就须要增长buffer cache的大小了。

   在得到了这些统计信息之后,咱们能够计算buffer cache的命中率:
   
   
   
   
1Hit Ratio = 1 – (physical reads – physical reads direct - physical reads direct (lob) ) / session logical reads 2Miss ratio = (physical reads – physical reads direct - physical reads direct (lob) ) / session logical reads
 
   一般在OLTP下,hit ratio应该高于0.9。不然若是低于0.9则须要增长buffer cache的大小。在考虑
 调整buffer cache hit ratio时,须要注意如下几点。
    1) 若是上次增长buffer cache的大小之后,没有对提升hit ratio产生很大效果的话,不要盲目增长buffer cache的大小以提升性能。由于对于排序操做或并行读,oracle是绕过buffer cache进行的。
    2) 在调整buffer cache时,尽可能避免增长不少的内存而只是提升少许hit ratio的状况出现。
咱们还能够查询每种buffer cache的统计信息,主要关注的仍是consistent_gets和db_block_gets以及
 physical_reads的值。
   
   
   
   
SQL > SELECT name, block_size,physical_reads, db_block_gets,consistent_gets 2 FROM v$buffer_pool_statistics; NAME BLOCK_SIZE PHYSICAL_READS DB_BLOCK_GETS CONSISTENT_GETS -- ------------------ ---------- -------------- ------------- --------------- DEFAULT 8192 28978 719 77591 DEFAULT 16384 2 80 11
      v$sysstat中名称以DBWR开头的都是有关DBWR后台进程相关的统计信息。当DBWR进程写完脏数据块之后或者扫描完LRU链表之后更新这些统计信息。DBWR会基于被触发的频率以及所处理的内存数据块的数量与总内存数据块的数量的比例,来进行自我调整。咱们能够经过这些统计信息获得一些对当前DBWR运行状况的认识。
3 buffer cache的等待事件
    与buffer cache相关的等待事件包括:latch free、buffer busy waits、free buffer waits。曾经发生过的等
待事件能够从v$system_event(一个等待事件对应一行记录)和v$session_event(一个session一个等待事件对应一行记录)中看到。而当前系统正在经历的等待事件能够从v$session_wait看到。

3.1 latch free等待

     等待事件“latch free”中与buffer cache有关的有两类:cache buffers chains latch和cache buffers lru chain latch。在理解了上面所描述的有关buffer cache的内部管理机制之后,就应该很容易理解这两个latch产生的缘由。

    对于buffer cache中的每一个hash chain链表来讲,都会有一个名为cache buffers chains latch的latch来保护对hash chain的并发操做,这种latch一般也叫做hash latch或CBC latch。数据库中会有不少的cache buffers chains latch,每一个latch都叫作child cache buffers chains latch。一个child cache buffers chains latch会管理多个hash chain。前面咱们知道,hash chain的数量由一个隐藏参数:_db_block_hash_buckets决定。一样也有一个隐藏参数:_db_block_hash_latches来决定有多少个cache buffers chains latch来管理这些hash chain。该参数的缺省值由buffer cache中所含有的内存数据块的多少决定,当内存数据块的数量
     •少于2052个时,_db_block_hash_latches = power(2,trunc(log(2, 内存块数量 - 4) - 1))
    •多于131075个时,_db_block_hash_latches = power(2,trunc(log(2, db_block_buffers - 4) - 6))
    •位于2052与131075 buffers之间,_db_block_hash_latches = 1024
可使用下面的SQL语句来肯定当前系统的cache buffers chains latch的数量。
   
   
   
   
SQL > select count ( distinct (hladdr)) from x$bh; COUNT ( DISTINCT (HLADDR)) -- --------------------- 1024 SQL > select count ( * ) from v$latch_children where name = ' cache buffers chains ' ; COUNT ( * ) -- -------- 1024
     在知道了cache buffers chains latch的数量之后,咱们只须要用hash chain的数量除以latch的数量之后,就能够算出每一个latch管理多少个hash chain了。咱们将下面7532除以1024,就能够知道,当前的系统中,每一个latch大概对应8个hash chain。
   
   
   
   
SQL > select x.ksppinm, y.ksppstvl, x.ksppdesc 2 from x$ksppi x , x$ksppcv y 3 where x.indx = y.indx 4 and x.ksppinm like ' \_% ' escape ' \ ' 5 and ksppinm like ' %_db_block_hash_buckets% ' 6 ; KSPPINM KSPPSTVL KSPPDESC -- -------------------- -------- ------------------------------------- _db_block_hash_buckets 7523 Number of database block hash buckets
    当数据库在hash chain搜索须要的数据块时,必须先得到cache buffers chains latch。而后在扫描hash chain的过程当中会一直持有该latch,直到找到所要的数据块才会释放该latch。当有进程一直在扫描某条hash chain,而其余进程也要扫描相同的hash chain时,其余进程就必须等待类型为cache buffers chains latch的latch free等待事件。

    不够优化的SQL语句是致使cache buffers chains latch的主要缘由。若是SQL语句须要访问过多的内存数据块,那么必然会持有latch很长时间。找出逻辑读特别大的sql语句进行调整。v$sqlarea里那些buffer_gets/executions为较大值的SQL语句就是那些须要调整的SQL语句。这种方式不是颇有针对性,比较盲目。网上曾经有人提供了一个比较有针对性的、查找这种引发较为严重的cache buffers chains latch的SQL语句的方式,其原理是根据latch的地址,到x$bh中找对应的buffer header,x$bh的hladdr表示该buffer header所对应的latch地址。而后根据buffer header能够找到所对应的表的名称。最后能够到v$sqltext(也能够到stats$sqltext)中找到引用了这些表的SQL语句。我也列在这里。where条件中的rownum<10主要是为了避免要返回太多的行,只要可以处理掉前10个latch等待就能有很大改观。
   
   
   
   
select /*+ rule */ s.sql_text from x$bh a,dba_extents b, ( select * from ( select addr from v$latch_children where name = ' cache buffers chains ' order by sleeps desc ) where rownum < 11 ) c, v$sqltext s where a.hladdr = c.addr and a.dbarfil = b.relative_fno and a.dbablk between b.block_id and b.block_id + b.blocks and s.sql_text like ' % ' || b.segment_name || ' % ' and b.segment_type = ' TABLE ' order by s.hash_value,s.address,s.piece /
    还有一个缘由可能会引发cache buffers chains latch,就是热点数据块问题。这是指多个session重复访问一个或多个被同一个child cache buffers chains latch保护的内存数据块。这主要是应用程序的问题。大多数状况下,单纯增长child cache buffers chains latches的个数对提升性能没有做用。这是由于内存数据块是根据数据块地址以及hash chain的个数来进行hash运算从而获得具体的hash chain的,而不是根据child cache buffers chains latches的个数。若是数据块的地址以及hash chain的个数保持一致,那么热点块仍然颇有可能会被hash到同一个child cache buffers chains latch上。能够经过v$session_wait的p1raw字段来判断latch free等待事件是不是因为出现了热点块。若是p1raw保持一致,那么说明session在等待同一个latch地址,系统存在热点块。固然也能够经过x$bh的tch来判断是否出现了热点块,该值越高则数据块越热。
   
   
   
   
SQL > select sid, p1raw, p2, p3, seconds_in_wait, wait_time, state 2 from v$session_wait 3 where event = ' latch free ' 4 order by p2, p1raw; SID P1RAW P2 P3 SECONDS_IN_WAIT WAIT_TIME STATE -- -- -------- --- --- --------------- ---------- ------------------ 38 6666535C 13 1 1 2 WAITED KNOWN TIME 42 6666535C 13 1 1 2 WAITED KNOWN TIME 44 6666535C 13 3 1 4 WAITED KNOWN TIME ……………………… 85 6666535C 13 3 1 12 WAITED KNOWN TIME 214 6666535C 138 1 1 2 WAITED KNOWN TIME
接下来,咱们就能够根据p1raw的值去找到所对应的内存数据块以及对应的表的名称了。
   
   
   
   
select a.hladdr, a. file #, a.dbablk, a.tch, a.obj, b. object_name from x$bh a, dba_objects b where (a.obj = b. object_id or a.obj = b.data_object_id) and a.hladdr = ' 6666535C ' ;
要解决热点块的问题,能够经过将热点块中的行分散到多个数据块中去,这样原来的热点块就变成了多个数据块,这样被hash到同一个latch的概率就下降了。若是热点块属于表,则能够先将表的数据导出来,而后增长表的pctfree值,最后将数据再导入。若是热点块属于索引,则能够设定较高的 pctfree参数后,重建索引。注意,这会增长索引的高度。

    经过前面咱们已经知道,每一个working set都会有一个名为cache buffers lru chain的latch(也叫作lru latch)来管理。任何要访问working set的进程都必须先得到cache buffers lru chain latch。cache buffers lru chain latch争用也是因为低效的扫描过多的内存数据块的SQL语句引发的。调整这些语句以下降逻辑读和物理读。只要修改一下上面找引发cache buffers chains latch的SQL语句便可找到这样的SQL语句。
   
   
   
   
select /*+ rule */ s.sql_text from x$bh a,dba_extents b, ( select * from ( select addr from v$latch_children where name = ' cache buffers lru chain ' order by sleeps desc ) where rownum < 11 ) c, v$sqltext s where a.hladdr = c.addr and a.dbarfil = b.relative_fno and a.dbablk between b.block_id and b.block_id + b.blocks and s.sql_text like ' % ' || b.segment_name || ' % ' and b.segment_type = ' TABLE ' order by s.hash_value,s.address,s.piece /
3.2 buffer busy waits等待

      当一个session在读取或修改buffer cache里的内存数据块时,首先必须得到cache buffers chains latch,得到之后,到hash chain上遍历直到找到须要的buffer header后。这时,该session必须在该buffer header上以share或exclusive模式(具体哪一个模式由该session的操做决定)得到一个buffer lock或一个buffer pin。一旦buffer header被pin住,session就将释放cache buffers chains latch,而后能够在该buffer上进行操做了。若是没法得到buffer pin,那么该session就会等待buffer busy waits等待事件。该等待事件不会出如今session的私有PGA里。

     buffer busy waits等待事件不能像latch free等待那样能够相对比较容易的进行过后跟踪。对于该等待事件,oracle提供了v$waitstat视图。v$waitstat里的记录都是buffer busy waits等待事件发生时进行更新的。也就是说,该视图体现的都是buffer busy waits等待事件的统计数据。但这只能给你提供一个大概的buffer busy waits的分布。若是要想具体的诊断该等待事件,只能当发生该等待时,到v$session_wait里去找缘由,从而才能找到解决的办法。处理buffer busy wait等待事件时,首先使用下面的SQL语句找到发生等待的数据块类别以及对应的segment。
   
   
   
   
select ' Segment Header ' class, a.segment_type, a.segment_name, a.partition_name from dba_segments a, v$session_wait b where a.header_file = b.p1 and a.header_block = b.p2 and b.event = ' buffer busy waits ' union select ' Freelist Groups ' class, a.segment_type, a.segment_name, a.partition_name from dba_segments a, v$session_wait b where b.p2 between a.header_block + 1 and (a.header_block + a.freelist_groups) and a.header_file = b.p1 and a.freelist_groups > 1 and b.event = ' buffer busy waits ' union select a.segment_type || ' block ' class, a.segment_type, a.segment_name, a.partition_name from dba_extents a, v$session_wait b where b.p2 between a.block_id and a.block_id + a.blocks - 1 and a. file_id = b.p1 and b.event = ' buffer busy waits ' and not exists ( select 1 from dba_segments where header_file = b.p1 and header_block = b.p2);
  而后,根据不一样的数据块类型进行相应的处理。
     1) 若是数据块类型为data block,若是版本为10g以前,则能够同时参照p3列的值来共同诊断。若是p3为130意味着同时有不少session在访问同一个data block,并且该data block没有在内存里,而必须从磁盘上获取。有三种方法能够下降该事件出现的频率:
    a、下降并发性。这个比较难实现。
    b、找出并优化含有这些segment的SQL语句,以下降物理和逻辑读。
    c、增长freelists和freelist groups。
若是没有足够的freelists,当同时对同一个表进行insert时,这就很容易引发buffer busy waits等待。若是正在等待buffer busy waits的session正在进行insert操做,那么须要检查如下那个表有多少freelists了。固然,因为freelists的不足主要会致使对于segment header的buffer busy waits等待。

    若是p3为220意味着有多个session同时修改在一个block(该block已经被读入内存了)里的不一样的行。这种状况一般出如今高DML并发性的环境里。有三种方法能够下降该事件出现的频率:
    a、下降并发性。这个比较难实现。
    b、经过增长pctfree减小block里含有的行数。
    c、将该对象移到拥有较小block尺寸的表空间里(9i或以上)。

   2) 若是数据块类型为data segment header(表或索引的segment header,不是undo segment header)上发生buffer busy waits等待事件,一般代表数据库里有些表或索引的段头具备频繁的活动。
进程访问segment header主要有两种缘由:一是得到或修改process freelists信息;二是扩展HWM。有三种方法能够下降该事件出现的频率:
    a、增长争用对象的freelists和freelist groups的数量。
    b、肯定pctfree和pctused之间的间隔不要过小。
    c、确保next extent的尺寸不要过小。
    d、9i之后,使用ASSM特性来管理block。

    3) 若是数据块类型为undo segment headers的争用等待,代表数据库中的rollback segments太少,或者他们的extent size过小,致使对于同一个segment header的大量更新。若是使用了9i之后的auto undo management,则不用处理,由于oracle会根据须要自动建立新的undo segments。若是是9i以前,则能够建立新的private rollback segments,并把它们online,或者经过下降transactions_per_rollback_segment参数来减轻该等待。

    4) 若是数据块类型为undo block,说明有多个session同时访问那些被更新过的block。这是应用系统的问题,在数据库来讲对此无能为力。
3.3 buffer busy waits等待

    在一个数据块被读入buffer cache以前,oracle进程必须为该数据块得到一个对应的可用的内存数
据块。当session在LRU list上没法发现一个可用的内存数据块或者搜寻可用的内存数据块被暂停的时候,该session就必须等待free buffer waits事件。

   从前面的描述,咱们已经知道,一个须要可用内存数据块的前台进程会连续扫描LRU 链表,直到达到一个限定值(也就是隐藏参数_db_block_max_scan_pct所指定的值,表示已经扫描的buffer header数量占整个LRU链表上的buffer header的总数量,在9i中该限定值为40%)。若是到该限定值时还没找到可用内存数据块时,该前台进程就会触发DBWR进程以便清空一些脏数据块,从而使得在辅助LRU链表上可以挂上一些可用的内存数据块。在DBWR进程工做时,该前台进程就必须等待free buffer waits。

    oracle跟踪每次对于可用的内存数据块的请求次数(记录在v$sysstat里的free buffer requested),也跟踪每次请求可用的内存数据块失败的次数(记录在v$system_event里的free buffer waits的total_waits)。而v$sysstat里的free buffer inspected则说明oracle为了找到可用的内存数据块所所跳过的数据块的个数,若是buffer cache很空,有不少空的数据块的话,则该值为0。若是free buffer inspected相对free buffer requested来讲很高,则说明oracle进程须要扫描更多的LRU链表上的数据块才能够找到可用的数据块。
   
   
   
   
SQL > select * 2 from v$sysstat 3 where name in ( ' free buffer requested ' , ' free buffer inspected ' ); STATISTIC# NAME CLASS VALUE -- -------- ------------------------------ ----------- ---------- 75 free buffer requested 8 290532493 79 free buffer inspected 8 2983596 SQL > select * 2 from v$system_event 3 where event = ' free buffer waits ' ; EVENT TOTAL_WAITS TOTAL_TIMEOUTS TIME_WAITED AVERAGE_WAIT TIME_WAITED_MICRO -- --------------- ----------- -------------- ----------- ------------ ----------------- free buffer waits 1003 476 71075 71 710749256
   能够看到,该系统的free buffer waits等待不多,总共等待的时间才0.476秒。同时也能够看到,请求了290532493(free buffer requested)个可用的内存数据块,可是在这个过程当中只跳过了2983596(free buffer inspected)个数据块,两者相差2个数量级。说明系统很容易就找到可用的内存数据块。    若是一个session花费了不少的时间等待free buffer waits等待事件的话,一般可能有如下缘由:     1) 低效率的SQL语句:对于那些引发很大逻辑读的SQL语句(v$sql里的disk_reads),那些SQL语句可能进行了全表扫描,索引全扫描、或者经过了不正确的索引扫描表等。调整这些SQL语句以下降逻辑读。     2) DBWR进程不够多:也能够经过增长DBWR checkpoints的个数来下降free buffer waits。9i下,能够经过减少fast_start_mttr_target参数来缩短MTTR,从而增长DBWR进程启动的次数。然而,这也有可能引发进程等待write complete waits事件。     3) I/O子系统太慢。     4) 延迟的块清除(block clearouts):一般发生的情形是,晚上向数据库导入了一个很大的表。而后早上运行应用系统时,会发现有有进程在等待buffer busy waits。这是由于第一个访问该表的进程将进行一个延迟的块清除,而这会致使free buffer waits等待事件。解决方法是在导入表完毕之后,执行一句全表扫描,好比一般是:select count(*) from该大表。这样在后面的进程再次访问的时候就不会产生free buffer waits等待事件了。     5) buffer cache过小:遇到free buffer waits事件,首先想到的就是增长buffer cache的大小。
相关文章
相关标签/搜索