Oracle内核技术揭密_吕海波 学习笔记
一,HASH链表与逻辑读
oracle要从高速缓冲区中拿到5号文件1234号块buffer的信息,就需要使用到HASH算法。
Buffer Cache:高速缓冲区中包含多个buffer,每一个buffer就记录一个数据块对应的缓冲信息。
Buffer Header:每一个Buffer Cache都有一个Buffer Header(BH),它用来记录这个高速缓冲区中所有的buffer address(BA),通过BA可以定位到缓冲区中的buffer。
Buffer Bucket:它里面只记录一系列Buffer Header双向链的链表头信息,oracle通过文件号和块号计算出HASH值,来定位到相应的Bucket,当不同Buffer Header下的buffer计算HASH值发生冲突时就会定位到同一个bucket,这时多个Buffer Header会构成一个双向链,叫cache buffer chain链表(CBC链表),并将链表头信息记录在bucket中。
Bucket数量由隐藏参数_db_block_hash_buckets决定:
SQL> SELECT ksppinm, ksppstvl, ksppdesc FROM x$ksppi x, x$ksppcv y WHERE x.indx = y.indx AND ksppinm = '_db_block_hash_buckets';
KSPPINM KSPPSTVL KSPPDESC
---------------------- --------- -------------------------------------
_db_block_hash_buckets 262144 Number of database block hash buckets
搜索buffer步骤如下:
1,进程根据要访问块的文件号和块号,计算HASH值得到到数字X。
2,根据HASH值X,定位到相应HASH Bucket,如BucketX。
3,搜索Bucket后的链表,查找哪个Buffer Header(BH)是目标BH。
4,找到目标BH后,从中取出Buffer的Buffer Address(BA)。
5,按BA访问Buffer
上述为oracle逻辑读的过程,如果搜索Bucket后的BH链表,没有找到包含目标块的BH,就说明目标块不在缓存中,只能物理读了。
二,catch buffer chain latch 与buffer pin 锁
SGA是公共内存,因此在访问buffer时是需要锁保护机制的,oracle采用的锁机制是latch和mutex。
正常情况下一个bucket后面的catch buffer chain就需要一个catch buffer chain latch来保护,但latch也是占用空间的,于是oracle这里是一个CBC latch负责多个bucket的锁管理。一个latch的大小为192字节:
SQL> select to_number(b.addr,'xxxxxxxxxxxxxxxxxxxxx')-
to_number(a.addr,'xxxxxxxxxxxxxxxxxxxxx')
from (select rownum rid,addr from v$latch_children where name='cache buffers chains' order by addr) a,
(select rownum rid,addr from v$latch_children where name='cache buffers chains' order by addr) b
where a.rid=b.rid+1 and rownum <=1;
TO_NUMBER(B.ADDR,'XXXXXXXXXXXXXXXXXXXXX')-TO_NUMBER(A.ADDR,'XXXXXXXXXXXXXXXXXXXX
--------------------------------------------------------------------------------
192
当我们搜索CBC链表时,需要先获取CBC latch进行锁保护;当找到目标所在BH,访问buffer前要修改buffer pin进行锁保护,修改完buffer pin后就可以释放CBC latch ,后续的buffer访问只需要在buffer pin保护下进行即可;当访问完buffer需要释放buffer pin时,这时又需要CBC latch 来保护。CBC latch的功能就是保护CBC链的访问和buffer pin的修改。
buffer pin锁有多种模式,常见的为共享模式(S)和独占模式(X)。在没有加锁时buffer pin值为0。
CBC latch也有共享和独占两种模式,模式的选择取决于以下4个要素:
1)对象类型(唯一索引,非唯一索引等)
2)块类型(根块,叶块,表块等)
3)操作(读,修改)
4)访问路径
对于一般的逻辑读,我们在读取buffer信息前都需要修改buffer pin值,这时的CBC latch基本都是独占锁。步骤如下:
当我们要访问索引的叶块时,需要频繁的访问它的根块和枝块,但对根块和枝块修改的频繁度又很低,这时没有必要使用独占模式的CBC latch。优化后的步骤如下:
可以看到在CBC latch 共享模式中搜索CBC链表和访问buffer,中间没有修改buffer pin,也没有释放CBC latch,这样CBC latch的加载时间比独占模式要长,但它缓解了独占模式下的争用。
在有唯一索引、唯一索引扫描情况时,根块、枝块、叶块、表块都是使用共享模式的CBC latch。也就是说当唯一索引使用‘where id = ?’这样的等值条件查询时,就使用共享模式的CBC latch;当使用的是范围查询时,则和非唯一索引一样,只有根块、枝块共享模式的CBC latch,不修改buffer pin,而叶块、表块还是使用独占模式的CBC latch。可以理解为使用索引唯一访问路径时,所有块的访问都是共享模式的CBC latch;另外,rowid方式直接逻辑读表块时是使用独占模式的CBC latch。
除了唯一索引外,在多数情况下,读或者写表块,都是使用独占模式的CBC latch。
另外根块和枝块,只要不修改,就使用共享模式的CBC latch。