1.4.4 oracle Latch 说明

目录

 

1.4.4 Latch 说明 

1.4.4.1  Latch 

1.4.4.2   有关 SPin  的说明 

1.4.4.3   进程获取 Latch 的过程

1.4.4.4   Latch  和  Lock 

1.4.4.5  Latch  争用 

 1.4.4.5.1  共享池中的 Latch 争用 

 1.4.4.5.2  数据缓冲池 Latch 争用 

1.4.4.6   热块产生的原因 

1.4.4.6.1  表数据块 

 1.4.4.6.2  索引数据块 

1.4.4.6.3  索引根数据块

1.4.4.6.4   段头数据块

1.4.4.7   检查 Latch  的相关 SQL 

1.4.4.7.1  查看造成 LATCH BUFFER CACHE CHAINS 等待事件的热快 

 1.4.4.7.2 查询当前数据库最繁忙的 Buffer,TCH(Touch)表示访问次数越高,热点快竞争问题就存在 

 1.4.4.7.3 查询当前数据库最繁忙的 Buffer,结合 dba_extents 查询得到这些热点Buffer来自哪些对象 

1.4.4.7.4  如果在Top 5中发现 latch free热点块事件时,可以从 V$latch_children中查询具体的子 Latch信息  

 1.4.4.7.5  获取当前持有最热点数据块的 Latch和 buffer信息

 1.4.4.7.6  利用前面的 SQL 可以找到这些热点 Buffer 的对象信息 

1.4.4.7.7  结合 SQL 视图可以找到操作这些对象的相关 SQL,然后通过优化 SQL减少数据的访问,或者优化某些容易引起争用的操作(如 connect by 等操作)来减少热点块竞争 


1.4.4 Latch 说明 

1.4.4.1  Latch 

       Latch属于System Lock, 用于保护SGA区中共享数据结构的一种串行化锁定机制。Latch 的实现是与操作系统相关的,尤其和一个进程是需要等待一个latch、需要等待多长时间有关。

       Latch 是Oracle提供的轻量级锁资源, 是一种能够极快地被获取和释放的锁,能快速,短时间的锁定资源,防止多个并发进程同时修改访问某个共享资源,它只工作在SGA中,通常用于保护描述 buffer cache 中block的数据结构。

       比如 SGA中,各种数据被反复从磁盘读取到内存,又被重新写回到磁盘上,如果有并发的用户做相同的事情,Oracle必须使用一种机制,来保证数据在读取的时候,只能由一个会话来完成,这种保护机制就是 Latch。

       并发(concurrency):是说有超过两个以上的用户对同样的数据做修改(可能包括插入,删除和修改)。

       并行(parallel):是说将一件事情分成很多小部分,让每一部分同时执行,最后将执行结果汇总成最终结果。 

       与每个latch相联系的还有一个清除过程,当持有latch的进程成为死进程时,该清除过程就会被调用。Latch 还具有相关级别,用于防止死锁,一旦一个进程在某个级别上得到一个 latch,它就不可能再获得等同或低于该级别的 latch。

Latch 不会造成阻塞,只会导致等待。 阻塞是一种系统设计上的问题,等待是一种系统资源争用的问题

1.4.4.2   有关 SPin  的说明 

比如数据缓存中的某个块要被读取,我们会获得这个块的  latch,这个过程叫做spin,另外一个进程恰好要修改这个块,他也要 spin 这个块,此时他必须等待,当前一个进程释放 latch后才能spin 住,然后修改,如果多个进程同时请求的话,他们之间将出现竞争,没有一个入队机制,一旦前面进程释放所定,后面的进程就蜂拥而上,没有先来后到的概念,并且这一切都发生的非常快,因为Latch 的特点是快而短暂

SPIN 与休眠(sleep) : 

休眠意味着暂时的放弃 CPU,进行上下文切换(context switch),这样 CPU 要保存当前进程运行时的一些状态信息,比如堆栈,信号量等数据结构,然后引入后续进程的状态信息,处理完后再切换回原来的进程状态,这个过程如果频繁的发生在一个高事务,高并发进程的处理系统里面,将是个很昂贵的资源消耗,所以Oracle选择了 spin,让进程继续占有 CPU,运行一些空指令,之后继续请求,继续 spin,直到达到_spin_count 值,这时会放弃 CPU,进行短暂的休眠,再继续刚才的动作。

1.4.4.3   进程获取 Latch 的过程

任何时候,只有一个进程可以访问内存中的某一个数据块,如果进程因为别的进程正占用块而无法获得 Latch 时,他会对 CPU 进行一次spin(旋转),时间非常的短暂, spin 过后继续获取,不成功仍然 spin,直到  spin 次数到达阀值限制(这个由隐含参数_spin_count 指定),此时进程会停止 spin,进行短期的休眠,休眠过后会继续刚才的动作,直到获取块上的 Latch 为止。 

进程休眠的时间也是存在算法的,他会随着 spin 次数而递增,以厘秒为单位,如1, 1, 2, 2, 4, 4, 8, 8,。。。休眠的阀值限制由隐含参数 _max_exponential_sleep  控制,默认是 2 秒,如果当前进程已经占用了别的 Latch,则他的休眠时间不会太长(过长会引起别的进程的 Latch 等待),此时的休眠最大时间有隐含参数_max_sleep_holding_latch 决定,默认是4厘秒。这种时间限制的休眠又称为短期等待

另外一种情况是长期等待锁存器(Latch Wait Posting),此时等待进程请求Latch 不成功,进入休眠,他会向锁存器等待链表(Latch Wait List)压入一条信号,表示获取 Latch的请求,当占用进程释放 Latch 时会检查 Latch Wait List,向请求的进程传递一个信号,激活休眠的进程。Latch Wait List 是在SGA区维护的一个进程列表,他也需要 Latch 来保证其正常运行,默认情况下 share pool latch 和library cache latch 是采用这个机制

如果将隐含参数_latch_wait_posting设置为 2,则所有 Latch都采用这种等待方式,使用这种方式能够比较精确的唤醒某个等待的进程,但维护 Latch Wait List需要系统资源,并且对 Latch Wait List 上 Latch 的竞争也可能出现瓶颈

如果一个进程请求,旋转,休眠 Latch 用了很长时间,他会通知 PMON 进程,查看 Latch 的占用进程是否已经意外终止或死亡,如果是则 PMON 会清除释放占用的 Latch资源。

总之,  Latch获取的流程:请求-SPIN-休眠-请求-SPIN-休眠  ... ... 占用。 

1.4.4.4   Latch  和  Lock 

从某种意义上说,Latch 是内存中的资源锁,数据库对象(表,索引等)的锁叫Lock。.

Latch 和 Lock的区别: 

(1). Latch是对内存数据结构提供互斥访问的一种机制,而 Lock 是以不同的模式来套取共享资源对象,各个模式间存在着兼容或排斥,从这点看出,Latch 的访问,包括查询也是互斥的,任何时候,只能有一个进程能 pin 住内存的某一块,幸好这个过程是相当的短暂,否则系统性能将没的保障,从 9I 开始,允许多个进程同时查询相同的内存块。

(2)Latch只作用于内存中,他只能被当前实例访问,而 Lock 作用于数据库对象,在RAC 体系中实例间允许 Lock 检测与访问 

(3)Latch是瞬间的占用,释放,Lock 的释放需要等到事务正确的结束,他占用的时间长短由事务大小决定 
(4)Latch是非入队的,而 Lock 是入队的 
(5)Latch不存在死锁,而 Lock 中存在

1.4.4.5  Latch  争用 

如果发现系统中经常由于 Lock  导致用户等待,这时需要考虑系统在逻辑设计上是否有问题,比如多用户对主键的删除或者修改,是否有用户使用 select  „ for update这样的语法,外键是否创建索引的因素。 这些因素是需要结合系统的业务逻辑性来进行数据库对象设计的。

如果发现系统慢是因为很多的 Latch 争用,就要考虑系统及数据库自身设计上是否存在问题,比如是否使用绑定变量,是否存在热快,数据存储参数设计是否合理等因素。

 导致 Latch 争用而等待的原因非常多,内存中很多资源都可能存在争用。

最常见的两类latch争用如下:

(1)共享池中的 Latch 争用。 
            (2)数据缓冲池中的 latch争用。

 1.4.4.5.1  共享池中的 Latch 争用 

   共享池中如果存在大量的 SQL被反复分析,就会造成很大的 Latch争用和长时间的等待,最常见的现象就是没有绑定变量。

最常见的集中共享池里的 Latch 是  library cache。 可以通过一下 SQL  来查询:


SQL> select * from v$latchname where name like 'library cache%'; 
 
    LATCH#   NAME                         HASH 
  ---------- -------------------------------------------------- ---------- 
        217 library cache                        3055961779 
        218 library cache lock                    916468430 
        219 library cache pin                     2802704141 
        220 library cache pin allocation             4107073322 
        221 library cache lock allocation            3971284477 
         222 library cache load lock                 2952162927 
        223 library cache hash chains               1130479025

          在分析系统性能时,如果看到有 library cache  这样的 Latch争用,就可以断定是共享池中出现了问题,这种问题基本是由 SQL 语句导致的,比如没有绑定变量或者一些存储过程被反复分析。

资源的争用可以通过如下 SQL  来查看:

SQL> select event,count(*) from v$session_wait group by event;

EVENT                                     COUNT(*) 

SQL*Ne tmessage from client                         4 
Streams AQ: waiting for messages in the queue           1 
ASM background timer                              1 
gcs remote message                    1 
ges remote message                         1 
jobq slave wait                         1 
rdbms ipc message                      14 
smon timer                        1 
pmon timer                          1 
Streams AQ: qmn slave idle wait                      1 
class slave wait                            1 
SQL*Net message to client                       1 
Streams AQ: waiting for time management or cleanup tasks  1 
Streams AQ: qmn coordinator idle wait              1 
DIAG idle wait                             1 
 
15 rows selected. 


 1.4.4.5.2  数据缓冲池 Latch 争用 

      访问频率非常高的数据块被称为热块(Hot Block),当很多用户一起去访问某几个数据块时,就会导致一些 Latch争用,最常见的latch争用有: 

(1)  buffer busy waits 
(2)  cache buffer chain 

这两个 Latch的争用分别发生在访问数据块的不同时刻。

Cache buffer chian产生原因: 
       当一个会话需要去访问一个内存块时,它首先要去一个像链表一样的结构中去搜索这个数据块是否在内存中,当会话访问这个链表的时候需要获得一个Latch,如果获取失败,将会产生 Latch cache buffer chain  等待,导致这个等待的原因是访问相同的数据块的会话太多或者这个列表太长(如果读到内存中的数据太多,需要管理数据块的 hash 列表就会很长,这样会话扫描列表的时间就会增加,持有chache buffer chain latch 的时间就会变长,其他会话获得这个 Latch 的机会就会降低,等待就会增加)。 

Buffer busy waits  产生原因: 
       当一个会话需要访问一个数据块,而这个数据块正在被另一个用户从磁盘读取到内存中或者这个数据块正在被另一个会话修改时,当前的会话就需要等待,就会产生一个buffer busy waits 等待。

       产生这些 Latch 争用的直接原因是太多的会话去访问相同的数据块导致热快问题,造成热快的原因可能是数据库设置导致或者重复执行的 SQL  频繁访问一些相同的数据块导致。 

     Latch 是简单的、低层次的序列化技术,用以保护 SGA中的共享数据结构,比如并发用户列表和 buffer cache里的blocks 信息。一个服务器进程或后台进程在开始操作或寻找一个共享数据结构之前必须获得对应的 latch,在完成以后释放latch。不必对latch 本身进行优化,如果latch 存在竞争,表明SGA 的一部分正在经历不正常的资源使用

1.4.4.6   热块产生的原因 

热快产生的原因不尽相同,按照数据块的类型,可以分成一下几种类型,不同热块类型处理的方式都是不同的。 
(1) 表数据块 
(2) 索引数据块 
(3) 索引根数据块 
(4) 文件头数据块 

1.4.4.6.1  表数据块 

        比如在 OLTP 系统中,对于一些小表,会给出某些数据块被频繁查询或者修改的操作,这时候,这些被频繁访问的数据块就会变成热块,导致内存中 Latch争用。

      如果出现这样热块的情况,并且表不太大,一个方法是可以考虑将表数据分布在更多的数据块上,减少数据块被多数会话同时访问的频率。

可以通过一下命令将每个数据块存放记录的数量减少到最少: 

       Alter table minimize records_per_block; 

       我们把数据分布到更多的数据块上,大大降低了一个数据块被重复读取的概率。 但是这种方法的缺点很明显,就是降低了数据的性能,在这种情况下,访问相同的数据意味着需要读取更多的数据块,性能会有所降低。 

 1.4.4.6.2  索引数据块 

        这样的情况通常发生在一个 RAC 架构里,某个表的索引键值出现典型的“右倾”现象,比如一个表的主键使用一个序列来生成键值,那么在这个主键在索引数据块上的键值就是以一种顺序递增的方式排列的,比如:1,2,3,4,5….,由于这些键值分布得非常接近,当许多用户在 RAC 的不同实例来向表中插入主键时,就会出现相同的索引数据块在不同实例的内存中被调用,形成一种数据块的争用,对于这种情况,使用反向索引可以缓解这种争用。  反向索引是将从前的索引键值按照反向的方式排列,在正常的主键 B-Tree 索引中,键值会按照大小的顺序排列,比如:1234,反向索引,键值就变成 4321. 

         这样,本来是放在相同的索引数据块上的键值,现在分布熬不同的数据块上,这样用户在 RAC 不同的实例上插入的主键值因为分布在不同的数据块上,所以不会导致热块的产生,这基本是反向索引被使用的唯一情况。 

        这样,本来是放在相同的索引数据块上的键值,现在分布熬不同的数据块上,这样用户在 RAC 不同的实例上插入的主键值因为分布在不同的数据块上,所以不会导致热块的产生,这基本是反向索引被使用的唯一情况。

  反向索引使用场合之所以如此受限制,是因为它丢弃了 B-Tree  索引的一个最重要的功能: 

  Index range scan 

 索引访问方式中,这个方式最常见,但是反向索引却不能使用这个功能,因为反向索引已经把键值的排列顺序打乱,当按照键值顺序查找一个范围时,在反向索引中,由于键值被反向存储,这些值已经不是连续存放的了。  所以 Index range scan  的方式在反向索引中没有任何意义。在反向索引中只能通过全表扫描或者全索引扫描的方式来实现。 这就是反向索引的一个非常严重的缺陷。 

1.4.4.6.3  索引根数据块

热块也可能发生在索引的根数据块上。 在 B-Tree索引里,当 Oracle  访问一个索引键值时,首先访问索引的根,然后是索引的分支,最后才是索引的叶块。索引的键值就是存储在叶块上面。 如图:

当索引的根,枝数据都集中在几个数据块上时,比如 D,G所在的枝数据块,当用户访问的范围从 A-F,都会访问这个数据块,如果很多用户频繁的访问这个范围的索引键值,有可能导致这个枝数据块变成热快

           当出现这种现象时,可以考虑对索引做分区,以便于使用这些根,枝数据块分布到不同的数据段(分区)上,减少数据块的并行访问的密度,从而避免由于索引根,枝数据块太集中导致热块产生。 

1.4.4.6.4   段头数据块

  在 Oracle 9i  之前,数据块的空间使用情况需要手工来管理,在每个数据段的段头有一个或者几个 Free list  列表,用于存放段中哪些数据块可以使用。   当数据块的数据达到数据块总容量的一个比例时(PCT_FREE  参数决定) ,数据块就会从Free list 中删除,这个数据块就不能在插入数据。   当数据块的空间减少到一个比例时(PCT_USED 参数决定) ,数据块就会被放到Free list 列表中,这些数据库可以被用来插入数据。   

  在OLTP系统中,一些数据段的Free List可能会是一个访问很频繁的数据块,比如这个数据库上有些表有很多删除,插入的动作,很多会话需要不断访问这些数据块的Free List 列表,以便获得需要的数据块信息。 此时这个数据块(称作段头数据块)就会变成一个热块,此时内存中就会出现如 cacha buffer chain 这样的 Latch 等待事件。 

当出现这个问题时,一个常见的解决方法是增加Free List 的数量,以便于分散会话访问数据块的密度。 

从Oracle 9i开始,引入了一个自动段管理的技术 ASSM(Automatic Segment Space Management:   ASSM),它让Oracle 自动管理“Free List”。  实际上在 ASSM里,已经没有Free List  这样的结构,Oracle  使用位图方式来标记数据块是否可用,这种数据块的空间管理方式比用一个列表来管理效率更高。 

对于 OLTP 系统,表的 DML操作非常密集,对于这些表,使用 ASSM 方式来管理会比人工管理更加方便和准确,能有效的避免段头变成热块。 

对于 OLAP 系统,这个参数并没有太大的实际意义,因为在这样的数据库中,很少有表发生频繁的修改,OLAP 系统主要的工作是报表和海量数据的批量加载。

1.4.4.7   检查 Latch  的相关 SQL 

1.4.4.7.1  查看造成 LATCH BUFFER CACHE CHAINS 等待事件的热快 

SELECT DISTINCT a.owner, a.segment_name 
   FROM dba_extents a, 
        (SELECT dbarfil, dbablk 
           FROM x$bh 
          WHERE hladdr IN (SELECT addr 
                             FROM (  SELECT addr 
                                       FROM v$latch_children 
                                   ORDER BY sleeps DESC) 
                             WHERE ROWNUM < 20)) b 
  WHERE     a.RELATIVE_FNO = b.dbarfil 
        AND a.BLOCK_ID <= b.dbablk 
        AND a.block_id + a.blocks > b.dbablk; 

 1.4.4.7.2 查询当前数据库最繁忙的 Buffer,TCH(Touch)表示访问次数越高,热点快竞争问题就存在 

SELECT * 
   FROM (  SELECT addr, 

                  ts#, 
                  file#, 
                  dbarfil, 
                  dbablk, 
                  tch 
             FROM x$bh 
         ORDER BY tch DESC) 
  WHERE ROWNUM < 11; 

 1.4.4.7.3 查询当前数据库最繁忙的 Buffer,结合 dba_extents 查询得到这些热点Buffer来自哪些对象 

SELECT e.owner, e.segment_name, e.segment_type 
            FROM dba_extents e, 
                 (SELECT * 
                    FROM (SELECT   addr, ts#, file#, dbarfil, dbablk, tch 
                               FROM x$bh 
                          ORDER BY tch DESC) 
                   WHERE ROWNUM < 11) b 
           WHERE e.relative_fno = b.dbarfil 
             AND e.block_id <= b.dbablk 
             AND e.block_id + e.blocks > b.dbablk; 

1.4.4.7.4  如果在Top 5中发现 latch free热点块事件时,可以从 V$latch_children中查询具体的子 Latch信息 
 


SELECT * 
   FROM (SELECT   addr, child#, gets, misses, sleeps, immediate_gets igets, 
                  immediate_misses imiss, spin_gets sgets 
             FROM v$latch_children 
            WHERE NAME = 'cache buffers chains' 
         ORDER BY sleeps DESC) 
  WHERE ROWNUM < 11; 

 1.4.4.7.5  获取当前持有最热点数据块的 Latch和 buffer信息

SELECT b.addr, a.ts#, a.dbarfil, a.dbablk, a.tch, b.gets, b.misses, b.sleeps 
   FROM (SELECT * 
           FROM (SELECT   addr, ts#, file#, dbarfil, dbablk, tch, hladdr 
                     FROM x$bh 
                 ORDER BY tch DESC) 

          WHERE ROWNUM < 11) a, 
        (SELECT addr, gets, misses, sleeps 
           FROM v$latch_children 
          WHERE NAME = 'cache buffers chains') b 
  WHERE a.hladdr = b.addr; 

 1.4.4.7.6  利用前面的 SQL 可以找到这些热点 Buffer 的对象信息 

SELECT distinct e.owner, e.segment_name, e.segment_type 
            FROM dba_extents e, 
                 (SELECT * 
                    FROM (SELECT   addr, ts#, file#, dbarfil, dbablk, tch 
                              FROM x$bh 
                           ORDER BY tch DESC) 
                   WHERE ROWNUM < 11) b 
           WHERE e.relative_fno = b.dbarfil 
             AND e.block_id <= b.dbablk 
             AND e.block_id + e.blocks > b.dbablk; 

1.4.4.7.7  结合 SQL 视图可以找到操作这些对象的相关 SQL,然后通过优化 SQL减少数据的访问,或者优化某些容易引起争用的操作(如 connect by 等操作)来减少热点块竞争 

break on hash_value skip 1 
SELECT /*+ rule */ hash_value,sql_text 
     FROM v$sqltext 
   WHERE (hash_value, address) IN ( 
             SELECT a.hash_value, a.address 
               FROM v$sqltext a, 
                     (SELECT DISTINCT a.owner, a.segment_name, a.segment_type 
                                FROM dba_extents a, 
                                     (SELECT dbarfil, dbablk 
                                        FROM (SELECT    dbarfil, dbablk 
                                                   FROM x$bh 
                                              ORDER BY tch DESC) 
                                      WHERE ROWNUM < 11) b 
                              WHERE a.relative_fno = b.dbarfil 
                                  AND a.block_id <= b.dbablk 
                                 AND a.block_id + a.blocks > b.dbablk) b 
             WHERE a.sql_text LIKE '%' || b.segment_name || '%' 
                AND b.segment_type = 'TABLE') 
ORDER BY hash_value, address, piece;

猜你喜欢

转载自blog.csdn.net/shangyuanlang/article/details/81904518