[ORACLE] oracle Buffer Cache 之Hash Bucket与Hash Chain List(cache bufferschain)

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

Hash Chain List可以通过基表x$bh查看   ??

Buffer Header 数据可以从数据库的字典表X$BH中查询得到 ??

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 的地址
 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的等待事件。