和BUFFER CACHE相关的主要Latch有:cache buffer lru chain、cache buffers chains
buffer cache就是一块含有许多数据块的内存区域,这些数据块主要都是数据文件里的数据块内容的拷贝,那么数据的查询和修改都要通过它来完成。接下来就看看访问数据的流程是怎样的
1 :当一个进程要访问数据时,首先要查找BUFFER CACHE看看数据是否已经存在?
(Y) 如果数据在BUFFER CACHE中存在,则根据数据的状态来判断是否可以直接访问还是需要构造一致性读取?
(N) 如果数据在BUFFER CACHE中不存在,则需要从数据文件中读取数据块到BUFFER CACHE中去。这个时候,需要查找在BUFFER CACHE中寻找足够的内存空间来读取相关的数据。
2 :现在BUFFER CACHE那么大,一个BUFFER一个BUFFER的扫描过去是相当的消耗资源以及查询的时间,那我们怎样才能快速地查到数据了? 可以看看下面关于BUFFER CACHE的示图:
2.1图中右边的有一块Buffers Memory,其中每一块小格就是一个Buffer(用来存放从数据文件中读取的数据块Block)
2.2图中左边的有许多Buffer Header用线指向Buffers Memory中的相应:Buffer。
2.3:图中左边的有许多实线箭头,这些箭头(其实就是数据结构的链表结构中的指针)将不同的Buffer Header连接成一条Hash Chain,这边也就是Cache Buffers Chain(双向链表。
2.4:另外,还有一个Hash: Bucket,其实这只是一个逻辑上的概念,即每一个HashBucket都会有一条Hash Chain来将Buffer Header(按照HASH算法分类后)连接起来,并由一个Cache Buffers Chains Latch来进行管理其并发操作。
每当将一个Block读入到Buffer Cache的时候,首先会构造一个与之对应的Buffer Header,然后根据HASH算法( Hash Bucket = MOD(Data Block Address, _DB_BLOCK_HASH_BUCKETS) ),将Buffer Header放到对应的Hash Bucket的Cache Buffers Chain中去,并在Buffer Header中记录如下信息
1).存放该Block在Buffer Cache中实际存储地址
2).存放该Block的类型(data,segment header,undo header,undo Block等类型)
3).由于此Buffer Header 所在的Hash Chain,是通过在Buffer Header保存指向前一个Buffer Header的指针和指向后一个Buffer Header的指针方式实现,所以还存指针
4).存储lru,lruw,ckptq等队列,一样是通过记录前后Buffer Header指针方式实现
5).当前该Buffer Header所对应的数据块的状态以及标记
6).该Buffer Header被访问的次数(touch次数)
7).正在等待该Buffer Header的进程列表(waiter list)及正在使用此Buffer Header的(user list)
2.5:最后,在看看Hash Latch,即Cache Buffers Chains Latch,在Oracle 8i之前,对应每一个Hash Bucket,Oracle使用一个独立的Latch来维护。Oracle 8i开始,Oracle增加了Hash Bucket的数量,这样每个Latch需要维护多个Bucket,由于每个Hash Bucket 上的Buffer Header数量大大减低,也使得Latch的性能反而提高。其中,BUFFER CACHE中Hash Bucket的个数由隐含参数_db_block_hash_buckets决定,Cache Buffers Chains Latch的个数由隐含参数_db_block_hash_latches决定。
SYS@ prod>select count(*) from x$bh; --用这句查出buffer的个数,
COUNT(*)
----------
13322
查询隐含参数可参考
select
x.ksppinm name,
y.ksppstvl value,
y.ksppstdf isdefault,
decode(bitand(y.ksppstvf,7),1,'MODIFIED',4,'SYSTEM_MOD','FALSE') ismod,
decode(bitand(y.ksppstvf,2),2,'TRUE','FALSE') isadj
from
sys.x$ksppi x,
sys.x$ksppcv y
where
x.inst_id = userenv('Instance') and
y.inst_id = userenv('Instance') and
x.indx = y.indx and
x.ksppinm like '%_&par%'
order by
translate(x.ksppinm, ' _', ' ')
/
NAME VALUE ISDEFAULT ISMOD ISADJ
-------------------------------------------------- --------- ---------- -----
_db_block_hash_buckets 32768 TRUE FALSE FALSE
_db_block_hash_latches 1024 TRUE FALSE FALSE
关于_db_block_hash_buckets参数的取值:据说在8i下,该参数缺省db_block_buffers×2;但是到了9i以后,该参数似乎取的是小于且最接近于db_block_buffers×2的素数。
在ORACLE 10G和11G中,默认值是大于2倍的buffer数量的最小的2的幂的值。举例如buffer数量是13322,2倍就是26644,那么大于26644的最小的2的幂的值是32768,也就是就会有32768个hashbucket。
一条hash chain上的bufferheader数量,没有固定限制(CR块有限制,一条hash chain上的CR块不能超过6个)。从隐含参数_db_block_max_cr_dba中可以查到这个限制:
NAME VALUE ISDEFAULT ISMOD ISADJ
-------------------------------------------------- --------- ---------- -----
_db_block_max_cr_dba 6 TRUE FALSE FALSE
判断是否有过长的hash chain的语句:过长的hash chain更容易引起热链进而引起CBC LATCH
SYS@ prod>select * from(select hladdr,count(*) from x$bh group by hladdr order by 2 desc) whererownum<5; ----------------x$bh.hladdr表示的是hash chainlatch address
HLADDR COUNT(*)
-------- ----------
3E2A7088 26
3E27D788 25
3E2AABE8 24
3E2BD9C0 24
hash latch:就是latch:cache buffers chains --CBC LATCH
用于保护hash chain结构,一个CBC LATCH管理着多个hash chain
1.服务进程需要扫描hash chain上的bufferheader时或者叫要读取buffer cache中数据块,
2.服务器进程要将新读入的数据块挂载到hash chain上时
SYS@ prod>select count(*) fromv$latch_children where name like '%cache buffers chains%';
COUNT(*)
----------
1024
每个pool会有至少8个“Latch:cachebuffers lru chain”.
NAME VALUE ISDEFAULT ISMOD ISADJ
-------------------------------------------------- --------- ---------- -----
_db_block_lru_latches 8 TRUE FALSE FALSE
working set与lru latch一一对应。lru latch的数量是由一个隐藏参数:_db_block_lru_latches决定的。该参数缺省值为DBWR进程的数量×8。该参数最小必须为8,如果强行设置比8小的数值,oracle将忽略你设置的值,而使用8作为该参数值。
每个working set都具有它自己的一组LRU和LRUW链表(LRU和LRUW链表总是成对出现的)。
ORACLE为了提高buffer cache性能(大内存),使用了多个working set
每个working set都由一个名为“Latch:cache buffers lru chain”的latch来保护,每一个lru latch对应一个working set。
而每个被加载到buffer cache的buffer header都以轮询的方式挂到working set上去。也就是说,当buffer cache加载一个新的数据块时,其对应的buffer header会去找一个可用的lru latch,如果没有找到,则再找下一个lru latch,直到找到为止。如果轮询完所有的lru latch也没能找到可用的lru latch,该进程只有等待latch free等待事件,同时出现在v$session_wait中,并增加“latch misses”。
如果启用了多个DBWR后台进程的话,每个DBWR进程都会对应一个不同的working set,而且每个DBWR只会处理分配给它的working set,不会处理其他的working set。
SYS@ prod>select name fromv$latch_children where name like '%buffers lru%' and gets != 0;
NAME
----------------------------------------------------------------
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
cache buffers lru chain
8 rows selected。
SYS@ prod>selectd.blk_size,c.child#,p.bp_name,c.gets,c.sleeps from x$kcbwds d,v$latch_childrenc,x$kcbwbpd p where d.set_latch=c.addr and d.set_id between p.bp_lo_sid and p.bp_hi_sidorder by c.child#;
BLK_SIZE CHILD# BP_NAME GETS SLEEPS
---------- ------------------------------ ---------- ----------
8192 1 KEEP 14 0
8192 3 RECYCLE 14 0
8192 5 DEFAULT 29310 0
2048 7 DEFAULT 14 0
4096 9 DEFAULT 14 0
8192 11 DEFAULT 14 0
16384 13 DEFAULT 14 0
32768 15 DEFAULT 14 0
8 rows selected.
总结:buffer cache中有pool,每个pool中至少有8个Latch:cachebuffers lru chain,每个Latch:cachebuffers lru chain对应着一个workingset,每个working set中,有多个CBC LATCH来,每个CBC LATCH管理着多个hash bucket,每个hash bucket对应着一条hash chain。
数据块在读入buffer cache中时,同时会在buffer cache中构造一个buffer header,ORACLE对数据块的DBA进行hash运算,根据运算结果将buffer header挂载到相应的hash chain上。
同时,每个working set中,有一对LRU和LRUW链表,buffer cache中的数据块,胜块会挂载到LRUW链表,其它块在LRU链表。也就是buffer cache中的数据块,肯定在LRU和LRUW链表之一:不能同时存在这两个链表上。
接--Buffer cache学习(三)