latch free 等待事件说明

当进程想要获取锁存器而此时该锁存器正被其他进程持有时产生Latch Free(锁存器空闲)等
待事件,类似于排队,Oracle使用锁存器来保护数据结构。一次只能在一个进程在获得锁存器后
修改或检查数据结构。其他需要访问该数据结构的进程必须等到它们获得锁存器后。不同于排队
的是,请求锁存器的进程不需要在队列中等待。如果获取锁存器失败,则进程仅仅等待一小段时
间就可以再次请求锁存器。这一小段等待时间成为“自旋”(spin)。如果在一次或多次自旋重复
(spin iterations)之后还没获取锁存器,进程就休眠一小段时间,然后再次尝试获取锁存器,
接下来休眠更长一段时间,直到获得锁存器。
    最常见的锁存器有:cache buffer chains(高速缓存缓冲区链)、library cache(高速缓存)
和shared pool(共享池)。
    1、等待参数
       latch free的等待参数描述如下:
       ◎ P1 进程等待的锁存器地址。
       ◎ P2 锁存器号,同v$latchname.latch#
          要查找等待锁存器的名称,可以是哟功能如下SQL语句:
          SELECT *
            FROM v$latchname
           WHERE latch# = &2_Value;
       ◎ P3 尝试的次数;显示进程试图获取锁存器的次数计数器。
      
    2、等待时间
       该时间的等待时间呈指数级增长。他不包含进程为锁存器自旋(spin) 花费的时间。

    以不等待模式获取的锁存器在IMMEDIATE_GETS和IMMEDIATE_MISSES列中统计。
    通过愿意等待模式获取锁存器在GETS和MISSES列中有统计。特殊锁存器的GETS列在每次进程
通过愿意等待模式请求该锁存器时递增。
    如果锁存器不能用,那么进程会在CPU上自旋(spin)一小段时间,重新尝试该锁存器。
    v$system_Event视图中的latch free等待事件的total_waits统计追踪进程以愿意等待模式
无法获得一个锁存器的次数。v$latch视图中特殊锁存器的sleeps统计追踪进程在锁存器上睡眠
的次数。因为进程在_SPIN_COUNT次数后无法获得锁存器时没有别的事可做只好睡眠,total_times
应该等于sleeps的综合,然而,很多时候total_times大于sleeps的总合。这是因为sleeps统计
只有在锁存器GET操作成功才更新,而不是每次尝试是更新。
    因为latch free等待事件总是很短,所以可以看到total_waits的一个较大的数字,这是计
数器在一段短时间内发生的等待事件。
   
    3、常见原因、诊断和动作
         锁存器征用表名另一个进程持有该锁存器过长的时间。当结合高需求量,这种争用带
     来的影响将会扩大,从而明显地降低性能。锁存器争用在高度并发的环境中很普遍。可以
     根据如下SQL查看进程竞争的热锁存器:
        SELECT NAME,
               Gets,
               Misses,
               Immediate_Gets,
               Immediate_Misses,
               Sleeps
          FROM V$latch
         ORDER BY Sleeps;
      
        ◎ shared pool锁存器和library cache锁存器的争用——解析
           shared pool锁存器和library cache锁存器的争用主要是由于紧密的硬解析。过多
           的硬解析通常出现在主要使用带有字面值的SQL语句的应用程序中。应解析是代价
           昂贵的才做,并且在解析期间必须持有子library cache锁存器。
            
           ①.使用下面语句查数据库中硬解析的数量:
               SELECT a.*,
                      SYSDATE - b.Startup_Time Days_Old
                 FROM V$sysstat  a,
                      V$instance b
                 WHERE a.NAME LIKE 'parse%';            
           ②.发现执行许多硬解析的当前会话:
               SELECT a.Sid,
                      c.Username,
                      b.NAME,
                      a.VALUE,
                      Round((SYSDATE - c.Logon_Time) * 24) Hours_Connected
                 FROM V$sesstat  a,
                      V$statname b,
                      V$session  c
                WHERE c.Sid = a.Sid
                  AND a.Statistic# = b.Statistic#
                  AND a.VALUE > 0
                  AND b.NAME = 'parse count (hard)'
                ORDER BY a.VALUE;
           ③.标识作为绑定变量的合适候选的literal SQL语句:
               SELECT Hash_Value,
                      Substr(Sql_Text, 1, 80)
                 FROM V$sqlarea
                WHERE Substr(Sql_Text, 1, 40) IN
                      (SELECT Substr(Sql_Text, 1, 40)
                         FROM V$sqlarea
                       HAVING COUNT(*) > 4
                        GROUP BY Substr(Sql_Text, 1, 40))
                ORDER BY Sql_Text;
           ④.正确的设置SESSION_CACHED_CURSOES参数:
               每当SQL语句到达时,Oracle检查该语句是否已经在库高速缓存中。如果是的话,
           只需要很少的开销就可以执行该语句;这种进程被称为软解析。在硬解吸不行时,软
           解析也不好。library cache锁存器在软解析中获得。Oracle仍然必须检查语句的语法
           和语义,除非该语句高速缓存在会话的游标高速缓存中。通过正确的设置
           SESSION_CACHED_CURSOES参数,可以减少library cache锁存器持有时间。然而最好的
           方法是减少软解析的数量,这只可通过应用程序完成。
               通过在v$sqlarea视图中查找带有大量parse_calls的语句,你可以找到令人不
           愉快的语句。
               如果发现数据库具有长共享池空闲列表,并且应用程序使用literal SQL,那么就
           应该考虑减少SHARED_POOL_SIZE。这将减少share pool锁存器的争用。
               也应该使用dbms_shared_pool.keep过程钉住共享池中可重用的对象。
               v$db_object_cache视图具有保存相关对象的信息。

        ◎ library cache锁存器的争用——带有高版本数的语句
           Oracle使用多个子游标来区分一些SQL语句,这些SQL语句具有相同的字符,但是不能
           被共享,因为他们引用不同的底层对象。例如:如果数据库有三个Customer表,并且
           每个表属于不同的模式,那么每个拥有者发布的SELECT * FROM Customer语句将具有
           相同的散列值,但是具有不同的子段数量。当被解析语句散列值匹配带有高的子段数
           或版本数的语句散列值时,Oracle必须比较语句和所有的现有版本。必须在检查期间
           持续持有library cache锁存器,着可能造成其他进程无法实现他们的library cache
           锁存器请求。通过在数据库中使用唯一的对象名,可以最小化这个问题。
           查询列出v$sqlarea中版本数大于20的所有SQL语句:
           SELECT Version_Count,
                  Sql_Text
             FROM V$sqlarea
            WHERE Version_Count > 20
            ORDER BY Version_Count,
                     Hash_Value;           

        ◎ cache buffer chains锁存器
           在将数据读入SGA时,他们的缓冲区头被防止在悬挂散列存储桶的链表中(散列链)。
           这种内存结构由大量子cache buffers chains锁存器(也称散列锁存器或CBC锁存器)
           保护。希望在添加、删除、搜索、检查、读取、或修改块的进程必须首先获得cache
           buffers chains锁存器,从而保护链上的缓冲区。这样就可以保证独占访问,并防
           止其它进程接下来读取或改变同一个链。为了完整性牺牲了并发性。通过设置
           _DB_BLOCK_HASH_LATCHS参数可以调整散列锁存器数量。
           如下SQL取实例中散列锁存器的数量:
           SELECT COUNT(DISTINCT(Hladdr)) FROM X$bh;
           
           ①.cache buffers chains锁存器的争用——底效率的SQL语句
               低效率的SQL语句是cache buffers chains锁存器争用的主要原因。当和高并发
           率混合在一起时,花费在latch free等待事件上的时间可能更多。在某些环境中,
           应用程序打开执行相同的低效率SQL语句的多个并发会话,并且这些SQL语句都没法得
           到相同的数据集,这种情况下相当普遍。
               如果集注下面3个方面,你可以做的很好:
               ◆ 每个逻辑读取需要一个latch gey操作和一个CPU
               ◆ 从latch get例程中获得的唯一方法是获得锁存器
               ◆ 在任意时刻,只有一个进程可以拥有cache buffer链,并且这个锁存器覆盖
                  许多数据块,另一个进程可能需要其中一些数据块。
           
           ②.cache buffers chains锁存器的争用——热块
               热块(hot block)是cache buffer chains锁存器争用的另一个常见原因。当多个
           会话重复访问一个或多个由同一个子chche buffer chains锁存器保护的块时,热块
           就产生。这主要是一种应用程序问题。在大多数情况下,增加cache buffer chains
           锁存器的数量对性能的改变没多大帮助。这是因为块被散列为散列存储桶和基于块地
           址和散列存储桶数量的链,而不是基于cache buffer chains锁存器的数量。如果块
           地址和散列存储桶的数量保持不变,则可能有少数热块仍然被一个cache buffer chains
           锁存器覆盖,除非锁存器数量极大的增加。
               捕获参与争用的SQL语句:
               -- Using the P1RAW from the above example (00000400837D7800).
               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 = '&1RAW_Value'
               UNION
               SELECT Hladdr,
                      File#,
                      Dbablk,
                      Tch,
                      Obj,
                      NULL
                 FROM X$bh
                WHERE Obj IN
                      (SELECT Obj
                         FROM X$bh
                        WHERE Hladdr = '&1RAW_Value'
                       MINUS
                       SELECT Object_Id
                         FROM Dba_Objects
                       MINUS
                       SELECT Data_Object_Id FROM Dba_Objects)
                  AND Hladdr = '&P1RAW_Value'
                ORDER BY 4;
               
               展开块的方法:
               ◆ 通过ROWID删除并且重新插入一些行
               ◆ 输出表,较大的增加PCTFREE,并且输入数据。这可以最小化每个块的行数量,
                  将他们展开到多个块上。当然这是以存储空间作为代价,并且全表扫描会更慢。
               ◆ 最小化表中每个块的记录数量。这涉及转储少量数据块,用于获得每个块的当
                  前理想行数。手工插入你确定合适的行数,然后发布命令:
                  ALTER TABLE table_name MINIMIZE RECORED_PER_BLOCK。截取表和输入数据。
               ◆ 对于索引,对于较高的PCTFREE值可以重建他们,需要注意的是这种方法可能
                  增加索引的高度。
               ◆ 考虑见效块大小。这将对全表扫描带来极大的负面影响。
               
           ③.cache buffers chains锁存器的争用——长散列链
               多个数据块可能被散列到同一个散列存储桶,他们和指针一起被连接到属于这个
           散列存储桶的散列链上。对于大型数据库,散列链上块的数量可以达到数百个。进程
           不得不顺序的扫描散列以获得所需的块,同时持有cache buffers chains锁存器不变。
           我们称之为“追赶链”。当扫描长链时,锁存器必须被持有更厂的时间,并且可能造
           成另一个进程无法实现她的cache buffers chains锁存器请求。
               一直到Oracle 8.0,我们可以很容易地确定特定的散列链长度,因为散列锁存器、
           散列存储桶和散列链之间的关系是1:1:1,散列链的长度等于有锁存器保护块的数量。
           下面查寻报告每个散列上块的数量,带有10个或更多块的链为长链。
               SELECT Hladdr,
                      COUNT(*)
                 FROM X$bh
                GROUP BY Hladdr
                ORDER BY 2;           
               通过使用_DB_BLOCK_HASH_BUSH_BUCKETS参数增加散列存储桶的数量,可以减少散
           列的长度:
           _DB_BLOCK_HASH_BUCKETS = 128021
           _DB_BLOCK_HASH_LATCHES = 1024
           Ratio = 128021 / 1024 = 125

LATCHES是一种低级的同步锁机制,用以维持某些查询和执行操作得顺序。通过它来达成对系统得SGA中共享内存结构的保护。当请求得到LATCH但它已被其他进程占住时,将产生一条FREE MISS记录。多数LATCH问题关联到不使用绑定变量(LIBRARY CACHE LATCH),重做日志生成问题(REDO ALLOCATION LATCH),内存缓冲区竞争问题(CACHE BUFFERS LRU CHAIN)和内存缓冲区中“过热”的数据块(CHCHE BUFFERS CHAIN)。当LATCH错失率超过0.5%时,需要仔细研究下了(METALINK ORACLE.COM/SUPPORT )。在STATSPACK报告中,等待事件LATCH FREE很高时,下面内容查看有关LATCH的问题。
        在Statspack报告中的Latch Activity 部分将可以得到关于这些Latch的更具体的信息。Get Requests、Pct Get Miss和Avg Slps/Miss(关于睡眠和错失)是针对“Willing-To-Wait”Latches请求的统计,而NoWait Requests和Pct NoWait Miss则是针对“No-Wait”Latches请求。相对于两种Latch请求的Pct Miss都应该接近于0.0。
        分析Latch问题时,v$latch视图十分有帮助,v$latchholder、v$latchname和v$latch_children等视图也很有帮助。
        当Statspack报告中,等待事件一节里的“Latch Free”项很高的数值时,在报告中关于Latch的段落内一定可以发现需要研究的问题。下面内容将可以查看这些有关Latch的问题。
        Library Cache and Shared Pool
        库缓存Latch为对库中对象的访问请求排队,每当执行SQL或PL/SQL存储过程、包、函数和触发器时,这个Latch即被用到。Parse操作中此Latch也会被频繁使用。自9i开始,增加了7个子Latch。共享池太小或SQL语句不能重用时,会发生“Shared Pool”、“Library Cache Pin”或“Library Cache”Latch竞争。SQL语句不可再用往往是因为未使用绑定变量,共享池里随处可见很相像但又不一样的SQL语句,而增加池的大小只会把Latch的问题搞得更糟。可以设置初始化参数CURSOR_SHARING=FORCE 减少绑定变量未使用的问题。然而,共享池和库缓存的Latch问题在后者对需处理的大批SQL语句而言设置太小,需要分配更多内存的情况下也会发生。欲将SQL或PL/SQL语句装入内存而首先释放部分空间的操作占据Latch,令其他用户等待。可以通过增大共享池来减缓此种竞争,还可以通过使用DBMS_Shared_pool.keep存储过程在内存种固定大的SQL或PL/SQL语句来解决这个问题。
        Redo Copy
        重做拷贝Latch的数量在缺省情况下是2*CPU_COUNT,但可通过_LOG_SIMULTANEOUS_COPIES的初始化参数来重新设置。增大此参数可以帮助减缓对重做拷贝Latch的竞争。重做拷贝Latch用来从PGA向重做日志缓冲区重做记录。
        Redo Allocation
        对Redo Allocation Latch(分配重做日志缓冲区中的空间)的竞争可以通过选用NOLOGGING的选项来减缓,该选项可以减轻日志缓冲区的负荷。另外,应当避免不必要的提交。
        Row Cache Objects
        “Row Cache Objects”Latch的竞争通常意味数据字典的竞争,它同时也可能是过度Parsing依赖于公共同义词SQL语句的症状。增大共享池一般可以解决此问题。这个办法往往用来解决库缓存Latch的问题,而在那个问题解决好了的前提下,“Row Cache Objects”Latch的竞争通常就不会成为一个问题。
        Cache Buffers Chains
        扫描SGA中的内存缓冲区时需要内存缓冲区链Latch。缓冲区中“过热”的数据块造成了缓冲区链Latch的问题,“过热”的数据块同时也是可能时调整差的SQL语句所导致的症状。“过热”的记录创建了“过热”的块,也就导致了块里其他记录的问题和映射到该块地址上的hash链的问题。为了定位“热块”,可以查询v$latch_children视图来确定块地址,并通过该视图与x$bh视图的连接来确定此Latch保护下的所有数据块(此将确定受“热块”影响的全部数据块)。以在x$bh视图里找到的file#和dbablk,可以进而查询dba_extents以确定受影响的对象。如果“热块”在某索引上,转而采用反向关键字索引即可把连续排放的记录搬到其他数据块中,它们也就不再会被“热块”一连串地锁住了。如果“热块”时索引的“根”块,那么反向索引就帮不上忙了,把_db_block_hash_buckets设置成比缓冲区数(DB_CACHE_SIZE/DB_BLOCK_SIZE)的2倍大的最小质数的值通常能够清除此问题的干扰。
        Cache Buffers LRU Chain
        扫描全部内存缓冲区块的LRU(最近最少使用)链时要用到内存缓冲区LRU链Latch。大小的内存缓冲区、过大的内存缓冲区吞吐量、过多的内存中进行的排序操作、DBWR速度跟不上工作负载等全都可能成为导致内存缓冲区LRU链Latch严重竞争的罪魁祸首:调整导致过量逻辑度的查询!可以增大初始化参数DB_BLOCK_LRU_LATCHES以得到多个LRU Latches,如此可减少竞争。非SMP(对称多处理器)系统仅需要惟一的LRU Latch,而在SMP系统的情况下,Oracle自动把LRU Latch的数目设置成1/2CPU数。对于一个数据库写进程,需要分配给它至少一个LRU Latch,如果需要增大数据库写进程的数目,则不能忘记同时增加LRU Latch的数目。
posted on 2009-08-31 00:25  一江水  阅读(10291)  评论(0编辑  收藏  举报