zoukankan      html  css  js  c++  java
  • [ORACLE] oracle Buffer Cache 之Hash Bucket与Hash Chain List(cache bufferschain)等待事件latch:cache buffers chains

    Hash Bucket 和 Cache Buffer Chain


    Oracle 把管理的所有的 Buffer 通过一个内部的 Hash 算法运算后存放到不同 Hash Bucket 中,这样通过 Hash Bucket 进行分割之后,众多的 Buffer被分布到一定数量的 Bucket之中,当用户需要在 Buffer中定位数据是否存在时,只需要通过同样的算法获得 Hash 值,然后到相应的 Bucket 中查找少量的 Buffer 即可确定。
    每个 Buffer 的存放的 Bucket 由 Buffer 的数据块地址(DBA,Data Block Address)运算决定。在 Bucket 内部,通过 Cache Buffer Chain(它是一个双向链表)将所有的 Buffer 通过 Buffer Header 信息联系起来。
    Buffer Header 存放的是对应数据块的概要信息,包括数据块的文件号、块地址、状态等。在判断数据块在 Buffer 中是否存在,通过检查 Buffer header 即可确定。
     

    ORACLE 将buffer cache中所有的buffer通过一个内部的Hash算法运算之后,将这些buffer放到不同的 Hash Bucket中。每一个Hash Bucket中都有一个Hash Chain List也叫:cache buffers chain,通过这个list,将这个Bucket中的block串联起来。对应每个Bucket,只存在一个Chain,当用户试图搜索Cache Buffer Chain时,必须首先获得Cache Buffer Chain Latch

    X$BH 与 Buffer Header

    Buffer Header 数据,可以从数据库的字典表中查询得到,这张字典表是:x$BH。X$BH中的 BH 就是指 Buffer Headers,每个 Buffer 在 x$bh 中都存在一条记录:
    Buffer Header 中存储每个 Buffer 容纳的数据块的文件号、块地址、状态等重要信息,可以将 Buffer Header 看作是 Buffer 的“名片”,通过这张名片,Buffer 的重要信息得以展现。

    SQL> set linesize 50
    SQL> desc x$bh
     Name             Null?      Type
     ----------------------- -------- ----------------
     ADDR             RAW(8)    ---block在buffercache中的address
     INDX             NUMBER
     INST_ID          NUMBER
     CON_ID           NUMBER
     HLADDR           RAW(8)    --(Hash Chain Latch Address)latch:cache buffers chains 的地址 
                    --
    这个字段可以和 v$latch_child.addr 进行关联,这样就可以把具体的 Latch 竞争和数据块关联起来,再结合dba_extents 视图,就可以找到具体的热点竞争对象。
                     --到具体热点竞争对象之后,可以进一步地结合 v$sqlarea 或者 v$sqltext 视图,找到频繁操作这些对象的 SQL,对其进行优化,就可以缓解或解决热点块竞争的问题。 BLSIZ NUMBER NXT_HASH RAW(8)    ---指向同一个Hash Chain List的下一个block地址  PRV_HASH RAW(8)    ---指向同一个HashChain List的上一个block地址 NXT_REPL RAW(8)    ---指向LRU list中的下一个block地址 PRV_REPL RAW(8)    ---指向LRUlist中的上一个block地址 ...... TS# NUMBER FILE# NUMBER ...... TCH NUMBER    --表征一个Buffer的访问次数,Buffer访问次数越多,说明该Buffer越“抢手”,也就越可能存在热点块竞争的问题 ......

    查询可以获得当前数据库最繁忙的Buffer:

    SQL> set linesize 200
    SQL> select *  from (select addr,ts#,file#,dbarfil,dbablk,tch from x$bh order by tch desc) where rownum<11; 
    
    ADDR            TS#     FILE#      DBARFIL     DBABLK        TCH
    ---------------- ---------- ---------- ---------- ---------- ----------
    00007F8DD59D2718      0         1        1      51224        255
    00007F8DD59D2128      0         1        1      51217        255
    00007F8DD59D2718      0         1        1    3917        255
    00007F8DD59D2128      0         1        1      48233        255
    00007F8DD59D5920      0         1        1      21927        255
    00007F8DD59D57A0      0         1        1    3915        255
    00007F8DD59D2128      0         1        1      20846        255
    00007F8DD59D2128      0         1        1      13657        255
    00007F8DD59D2718      0         1        1      51200        255
    00007F8DD59D22A8      0         1        1      48234        255
    
    10 rows selected.

    查询得到这些热点Buffer都来自哪些对象:

    SQL> col OWNER for A20
    SQL> 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+  2    3  e.blocks>b.dbablk; 
    
    OWNER             SEGMENT_NAME                                                              SEGMENT_TYPE
    -------------------- -------------------------------------------------------------------------------------------------------------------------------- ------------------
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    SYS             C_FILE#_BLOCK#                                                              CLUSTER
    
    10 rows selected.

    每个Buffer对应x$bh中一条记录,Hash Chain List就是由x$bh中的NXT_HASH,PRV_HASH 这2个指针构成了一个双向链表,通过NXT_HASH,PRV_HASH这2个指针,那么在同一个Hash Chain List的block就串联起来了。

     由于 Buffer 根据 Buffer Header 进行散列,最终决定存入那一个 Hash Bucket,那么 Hash Bucket 的数量在一定程度上决定了每个 Bucket 中 Buffer 数量的多少,也就间接影响了搜索的性能。所以在不同版本中,Oracle 一直在修改算法,优化 Hash Bucket 的数量。我们可以想象,

    Bucket 的数量多一些,那么在同一时间就可以有更多的同学可以拿到不同的抽屉,进行数据访问;但是更多的抽屉,显然需要更多的存放空间,更多的管理成本,所以优化在什么时候都不是简单的一元方程。
    Latch:cache buffers chains的数量受隐含参数_db_block_hash_latches的影响,
    Hash Bucket 的设置受一个隐含参数_ DB_BLOCK_HASH_BUCKETS 的影响。
    从上图可看到 一个latch:cache buffers chains(x$bh.hladdr) 可以保护多个Hash Bucket,也就是说,如果我要访问某个block,我首先要获得这个latch,一个Hash Bucket对应一个Hash Chain List,而这个Hash Chain List挂载了一个或者多个Buffer Header。
    SQL> select BANNER_FULL from v$version;
    
    BANNER_FULL
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------
    Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
    Version 19.3.0.0.0
    SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ FROM x$ksppi x,x$ksppcv y  WHERE x.inst_id =USERENV ('Instance')  AND y.inst_id =USERENV ('Instance')  AND x.indx = y.indx  AND x.ksppinm LIKE'%_db_block_hash%';
    
    NAME                      VALUE          DESCRIB
    -----------------------------------------------------------------------
    _db_block_hash_buckets    262144      Number of database block hash buckets
    _db_block_hash_latches    2048       Number of database block hash latches        
    SQL> select count(distinct hladdr) from x$bh ; 
    
    COUNT(DISTINCTHLADDR)
    ---------------------
             2048
    SQL> select count(*)  from v$latch_children a,v$latchname b where a.latch#=b.latch# and b.name='cache buffers chains'; 
    
      COUNT(*)
    ----------
          2048 

    验证

    SQL> select *  from(select hladdr,count(*) from x$bh  group by hladdr) where rownum<=5; 
    
    HLADDR           COUNT(*)
    ---------------- ----------
    00000000A80CE028     25
    00000000A80CE0F0     31
    00000000A80CE1B8     40
    00000000A80CE280     18
    00000000A80CE348     17
    
    SQL> select hladdr,obj,dbarfil,dbablk,nxt_hash,prv_hash from x$bh where hladdr='00000000A80CE028' order by obj;
    
    HLADDR            OBJ    DBARFIL       DBABLK NXT_HASH       PRV_HASH
    ---------------- ---------- ---------- ---------- ---------------- ----------------
    00000000A80CE028      2         1        12226 00000000A80CEC68 00000000A80CEC68
    00000000A80CE028     64         1       114311 00000000A80CE828 00000000A80CE828
    00000000A80CE028    119         1         1280 00000000D4F9A130 00000000CEFC4598
    00000000A80CE028    119         1         1280 00000000D1FC9E30 00000000A80CEBB8
    00000000A80CE028    119         1         1280 00000000D8F84740 00000000D1FC9E30
    00000000A80CE028    119         1         1280 00000000A80CEBB8 00000000D4F9A130
    00000000A80CE028    378         1        25756 00000000A80CEA38 00000000A80CEA38
    00000000A80CE028    385         1        21575 00000000A80CE878 00000000A80CE878
    00000000A80CE028    568         1         3864 00000000D9F57B78 00000000D2FC53A8
    00000000A80CE028    568         1         3864 00000000D5F995F0 00000000CFFC4700
    00000000A80CE028    568         1         3864 00000000D2FC53A8 00000000A80CE8E8
    00000000A80CE028    568         1         3864 00000000A80CE8E8 00000000D5F995F0
    00000000A80CE028    720         1       115908 00000000A80CECC8 00000000A80CECC8
    00000000A80CE028    780         1        14810 00000000E4FB9CD8 00000000A80CE998
    00000000A80CE028      20324         1        47648 00000000D0FDF550 00000000A80CEB98
    00000000A80CE028      20324         1        47648 00000000D3FAFB20 00000000CDFB50E8
    00000000A80CE028      20324         1        47648 00000000D6FEF6A8 00000000D0FDF550
    00000000A80CE028      20324         1        47648 00000000A80CEB98 00000000D3FAFB20
    00000000A80CE028      73154         3        64962 00000000A80CEBC8 00000000A80CEBC8
    00000000A80CE028      73610         3        78492 00000000A80CE998 00000000D6F864C8
    00000000A80CE028      74505         3       173812 00000000A80CE678 00000000A80CE678
    00000000A80CE028      74952         3       254615 00000000A80CECE8 00000000A80CECE8
    00000000A80CE028      74978         3       253018 00000000A80CE848 00000000A80CE848
    00000000A80CE028      75239         3       224361 00000000A80CE808 00000000A80CE808
    00000000A80CE028 4294967295         4         1752 00000000A80CEAF8 00000000A80CEAF8
    
    25 rows selected.
    当一个用户进程想要访问Block时:
    1>    对该Block运用Hash算法,得到Hash值。
    2>     获得cache buffers chains latch
    3>     到相应的Hash Bucket中搜寻相应Buffer Header
    4>       如果找到相应的Buffer Header,然后判断该Buffer的状态,看是否需要构造CR Block(一致性读块),或者Buffer处于pin的状态,最后读取。
    5>     如果找不到,就从磁盘读入到Buffer Cache中。
    从Oracle9i开始 cache buffers chains latch可以只读共享,也就是说用户进程A以只读(select)的方式访问Block,这个时候获得了该latch,同时用户进程B也以只读的方式访问Block不同的Block,但被同一个 cache buffers chains latch管理),那么这个时候由于是只读的访问,用户进程B也可以获得该latch。但是,如果用户进程B要以独占的方式访问Block,那么用户进程B就会等待用户进程A释放该latch,这个时候Oracle就会对用户进程B标记一个latch:cache buffers chains的等待事件。
     

    热点块竞争与解决

    Latch Free 是一个汇总等待事件,我们需要从 v$latch 视图获得具体的 Latch 竞争主要是由哪些 Latch 引起的。

    cache buffers chains 正是主要的 Latch 竞争。实际上,这个数据库在繁忙的时段,基本上处于停顿状态,大量进程等待 latch free 竞争,这些获得 Session 的等待事件可以很容易地从 v$session_wait 视图中查询得到

    如果需要具体确定热点对象,可以从 v$latch_children 中查询具体的子 Latch 信息,
    SQL> 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;  2  
    
    ADDR             CHILD#      GETS       MISSES     SLEEPS      IGETS      IMISS    SGETS
    ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
    00000000A80F1610     70      6399           12       4        152      0        8
    00000000A83ED658       1463      5310        2       2         89      0        0
    00000000A829F9C8    853      5439           10       2        129      0        8
    00000000A852DF60       2048      4980        0       0         88      0        0
    00000000A852DC40       2044      3831        0       0        101      0        0
    00000000A852DB78       2043     11789        0       0         87      0        0
    00000000A852DAB0       2042      1044        0       0         89      0        0
    00000000A852D9E8       2041      5321        0       0         95      0        0
    00000000A8529920       2040      2905        0       0         89      0        0
    00000000A8529858       2039      1736        0       0         93      0        0
    
    10 rows selected.
    获取当前持有最热点数据块的 Latch 及 Buffer 信息:
    SQL>  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;   
    
    ADDR            TS#    DBARFIL       DBABLK     TCH       GETS     MISSES     SLEEPS
    ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
    00000000A84DEAF8      0         1          178     218       2282      0        0
    00000000A80DFAB8      0         1          183     217       1625      0        0
    00000000A8438160      0         1          181     217       2158      0        0
    00000000A83D30D8      0         1          176     217       2556      0        0
    00000000A8330740      0         1          179     217       2096      0        0
    00000000A828DE70      0         1          182     217       2511      0        0
    00000000A8228DE8      0         1          177     217       2476      0        0
    00000000A8182450      0         1          180     217       3223      0        0
    00000000A82BA0D8      2         4        2     187       2560      0        0
    00000000A816C2B8      0         1          201     186      38016      2        0
    
    10 rows selected.
    找到这些热点 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;
    结合 v$sqltext 或 v$sqlarea,可以找到操作这些对象的相关 SQL,
    SQL> SELECT  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 upper(a.sql_text) LIKE '%' || b.segment_name || '%' AND b.segment_type = 'TABLE')
      2    3    4    5    6    7  ORDER BY hash_value, address, piece;
    
    HASH_VALUE SQL_TEXT
    ---------- ----------------------------------------------------------------
     103134026 INSERT INTO "SAPR3"."DBSTATIORA" (TNAME, INAME, ANDAT, VWTYP, IN
     103134026 DBL, INDBS, INDRU, INDDE) VALUES ('TRNL_E071~', 'TRNL_E071^0', '
     103134026 20200505171508', ' ', 0, 0, 0, 0)
     104653397 UPDATE "SAPR3"."DBSTATIORA" SET ANDAT = '20200505171547', VWTYP
     104653397 = ' ', INDBS = 43280, INDRU = 43280 WHERE TNAME = 'DDNTT~' AND I
     104653397 NAME = 'DDNTT~0'
    
     
  • 相关阅读:
    Linux常用命令英文全称与中文解释
    输入一个URL之后发生了什么?
    四种基本的数据结构
    关于深拷贝
    TCP的三次握手和四次挥手
    利用正则表达式去掉字符串的前后空格
    用canvas画一个等腰三角形
    三种隐藏元素方法的区别
    消息中间件-activemq安全机制
    Netty学习(十)-Netty文件上传
  • 原文地址:https://www.cnblogs.com/tingxin/p/12831375.html
Copyright © 2011-2022 走看看