oracle buffercache

  这两天抽空把Buffer Cache原理深入学习了下,感觉收获很多,整理了些资料大家一起分享下。

深入Buffer Cache之一——Buffer Cache概述

二、 Hash Bucket Hash Chain List cache bufferschain
Oracle将buffer cache中所有的buffer通过一个内部的Hash算法运算之后,将这些buffer放到不同的  Hash Bucket中。每一个 Hash Bucket中都有一个 Hash Chain List 也叫:cache buffers chain ,通过这个list,将这个Bucket中的block串联起来。
下面举个简单的例子来介绍一下Hash 算法,Oracle的Hash 算法肯定没这么简单,具体算法只有Oracle公司知道。
一个简单的mod函数 ,我们去mod 4
Ø  1mod 4 = 1
Ø  2mod 4 = 2
Ø  3mod 4 = 3
Ø  4mod 4 = 0
Ø  5mod 4 = 1
Ø  6mod 4 = 2
Ø  7mod 4 = 3
Ø  8mod 4 = 0
……………省略…………………..
那么这里就相当于创建了4个 Hash Bucket,如果有如下block:
blcok  BA(1,1)  ------> (1+1)mod 4 =2  
block  BA(1,2)  ------> (1+2)mod 4 =3
block  BA(1,3)  ------> (1+3)mod 4 =0
block  BA(1,4)  ------> (1+4)mod 4 =1
block  BA(1,5)  ------> (1+5)mod 5 =2
………........省略…………………....
比如我要访问block(1,5),那么我对它进行Hash运算,然后到Hash Bucket为2的这个Bucket里面去寻找,Hash Bucket 为2的这个Bucket 现在有2个block,这2个block是挂在 Hash Chain List上面的。
Hash Chain List到底是怎么组成的呢?这里我们就要提到x$bh这个基表了。
SQL>desc x$bh
Name               Null?    Type
------------------------ ----------------
ADDR                         RAW(8)    ---block在buffercache中的address
INDX                           NUMBER
INST_ID                     NUMBER
HLADDR                    RAW(8)    --(Hash Chain Latch Address)latch:cache buffers chains 的地址
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地址
TCH                             NUMBER  --表征一个Buffer的访问次数,Buffer访问次数越多,说明该Buffer越“抢手”,也就越可能存在热点块竞争的问题
………………省略…………………………
通过以下查询可以获得当前数据库最繁忙的Buffer:
SQL> select *
  2    from (selectaddr,ts#,file#,dbarfil,dbablk,tch
  3            from x$bh
  4           order by tch desc)
  5  where rownum<11;
ADDR            TS#      FILE#   DBARFIL     DBABLK        TCH
-------- ---------- ---------- ---------- ---------- ----------
0D2C9F74          0          1          1       1658     26393
0D2C9F74          0          1          1       1674     21681
0D2C9F74          0          1          1         92       6237
0D2C9F74          0          1          1        796       5853
0D2C9F74          0          1          1        797       5852
0D2C9F74          0          1          1       1666       5711
0D2C9F74          2          3          3     23632       5447
0D2C9F74          2          3          3     23627       5425
0D2C9F74          2          3          3     23629       5423
0D2C9F74          2          3          3     23631       5423
10 rows selected
         再结合DBA_EXTENTS中的信息,可以查询得到这些热点Buffer都来自哪些对象:
SQL> select e.owner,e.segment_name,e.segment_type
  2    from dba_extents e,
  3         (select *
  4            from (selectaddr,ts#,file#,dbarfil,dbablk,tch
  5                    from x$bh
  6                   order by tch desc)
  7           where rownum<11) b
  8  where e.relative_fno=b.dbarfil
  9     and e.block_id<=b.dbablk
10     and e.block_id+e.blocks>b.dbablk;
OWNER            SEGMENT_NAME                     SEGMENT_TYPE
-------------- --------------------------- ------------------
SYS                 JOB$                                 TABLE
SYSMAN              MGMT_JOB_EMD_STATUS_QUEUE       TABLE
SYSMAN             MGMT_JOB_EMD_STATUS_QUEUE       TABLE
SYSMAN             MGMT_JOB_EMD_STATUS_QUEUE       TABLE
SYSMAN             MGMT_JOB_EMD_STATUS_QUEUE       TABLE
SYSMAN              MGMT_JOB_EMD_STATUS_QUEUE       TABLE
SYSMAN             MGMT_JOB_EMD_STATUS_QUEUE      TABLE
SYSMAN              MGMT_METRIC_DEPENDENCY          TABLE
SYS                 I_JOB_NEXT                         INDEX
SYS                 _SYSSMU9$                          TYPE2 UNDO
10 rows selected
每个Buffer在x$bh中都存在一条记录, Hash Chain List就是由x$bh中的NXT_HASH,PRV_HASH 这2个指针构成了一个双向链表,其示意图如下:
oracle buffercache_第1张图片
通过NXT_HASH,PRV_HASH这2个指针,那么在同一个 Hash Chain List的block就串联起来了。
理解了 Hash Bucket  Hash Chain List,我们现在来看看 Hash Bucket 与  Hash Chain List管理Buffer Cache 的结构示意图:
oracle buffercache_第2张图片
从图中我们可以看到,一个 latch:cache buffers chains(x$bh.hladdr) 可以保护多个 Hash Bucket,也就是说,如果我要访问某个block,我首先要获得这个latch,一个 Hash Bucket对应一个 Hash Chain List,而这个 Hash Chain List挂载了一个或者多个Buffer Header。
HashBucket的数量受隐含参数_db_block_hash_buckets的影响,Latch:cache buffers chains的数量受隐含参数_db_block_hash_latches的影响,该隐含参数可以通过如下查询查看:
SQL> select * from v$version;
BANNER
------------------------------------------------------------------
OracleDatabase 10g Enterprise Edition Release 10.2.0.3.0 - 64 bi
SQL> SELECT x.ksppinm NAME, y.ksppstvlVALUE, x.ksppdesc describ
  2  FROM x$ksppi x,x$ksppcv y
  3  WHERE x.inst_id =USERENV ('Instance')
  4  AND y.inst_id =USERENV ('Instance')
  5  AND x.indx = y.indx
  6  AND x.ksppinm LIKE'%_db_block_hash%'
  7  /
NAME                      VALUE           DESCRIB
------------------------- -----------------------------------------------------
_db_block_hash_buckets    524288          Numberof database block hash buckets
_db_block_hash_latches    16384           Numberof database block hash latches
_db_block_hash_buckets 该隐含参数在8i以前 等于db_block_buffers/4的下一个素数 到了8i 该参数等于 db_block_buffers*2 ,到了9i 以后,该参数取的是小于且最接近 db_block_buffers*2 的一个素数。
_db_block_hash_latches 该隐含参数表示的是  cache buffers chains latch的数量,它怎么计算的我们不用深究。
可以看到,从8i以后 Hash Bucket数量比以前提升了8倍。可以用下面查询计算 cache buffers chains latch的数量:
方法一:
SQL>select count(*)
2   from v$latch_children a,v$latchname b
3  wherea.latch#=b.latch# and b.name='cache buffers chains';
  COUNT(*)
----------
     16384
方法二:
SQL>select count(distinct hladdr) from x$bh ;
COUNT(DISTINCTHLADDR)
---------------------
                16384
根据我们的查询,那么一个 cache buffers chains latch 平均下来要管理32个 Hash Bucket,那么现在我们随意的找一个latch,来验证一下前面提到的结构图:
SQL>select *
2   from(select hladdr,count(*) from x$bh  group by hladdr)
3  whererownum<=5;
HLADDR                  COUNT(*)
----------------------------------------
C000000469F08828         15
C000000469F088F0         14
C000000469F089B8         15
C000000469F08A80         24
C000000469F08B48         17
我们查询latch address 为C000000469F08828 所保护的data block:
SQL>select hladdr,obj,dbarfil,dbablk,nxt_hash,prv_hash
2  fromx$bh
3  wherehladdr='C000000469F08828'
4  orderby obj;
HLADDR                        OBJ      DBARFIL   DBABLK    NXT_HASH               PRV_HASH
----------------------------------------- ------------ ---------- -----------------------------------------------------------
C000000469F08828         2              388  322034     C0000004686ECBD0        C00000017EF8D658
C000000469F08828         2                388  396246   C0000004686ECA60      C0000004686ECA60
C000000469F08828         18               411 674831   C0000004686ECC00      C0000004686ECC00
C000000469F08828         216            411  438948   C0000004686ECBB0      C0000004686ECBB0
C000000469F08828         216            220  100217   C0000004686ECAA0      C0000004686ECAA0
C000000469F08828         216            220  60942      C000000151FB5DD8     C0000004686ECBD0
C000000469F08828         569            411  67655      C00000011FF81668       C0000001E8FB7AC0
C000000469F08828         569            280  1294        C0000004686ECB60      C000000177F9F078
C000000469F08828   58744570      210  332639  C000000177F9F078        C0000004686ECB60
C000000469F08828   65178270      254  408901  C0000004686ECBF0       C0000004686ECBF0
C000000469F08828   65347592      84     615093  C0000004686ECB90       C0000004686ECB90
C000000469F08828   65349200      765  1259399  C0000004686ECA70       C0000004686ECA70
请观察DBA(388,396246),它的NXT_HASH与PRV_HASH相同,也就是说DBA(388,396246)挂载在只包含有1个data block的  Hash Chain上。
另外也请注意,我通过count(*)计算出来的时候有15个block,但是查询之后就变成了12个block,那说明有3个block被刷到磁盘上了
当一个用户进程想要访问Block(569,411)时:
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以前,如果其它用户进程已经获得了这个latch,那么新的进程就必须等待,直到该用户进程搜索完毕(搜索完毕之后就会释放该latch)。
从Oracle9i开始  cache buffers chains latch可以只读共享,也就是说用户进程A以只读(select)的方式访问Block(84,615093),这个时候获得了该latch,同时用户进程B也以 只读的方式访问Block(765,1259399)(不同的Block,但被同一个 cache buffers chains latch管理),那么这个时候由于是只读的访问,用户进程B也可以获得该latch。但是,如果用户进程B要以 独占的方式访问Block(765,1259399),那么用户进程B就会等待用户进程A释放该latch,这个时候Oracle就会对用户进程B标记一个 latch:cache buffers chains的等待事件。

你可能感兴趣的:(oracle,oracle)