1.Buffer cache中的LRU链表概念:
oracle在hash chain中未搜索到所需要的buffer时,ORACEL 服务进程会发出I/O调用,到磁盘的数据文件中读取相应数据块--除了直接路径读外,此时会将数据块的内容拷贝到buffer cache 内存中--同时会构造一个buffer header。
在将数据块拷贝到buffer cache中时,假如buffer cache是空的,直接拿一个空的内存数据块来用即可。
但是如果buffer cache中的内存数据块全都被用掉了,没有空的内存数据块了,怎么办?这里就用到了LRU链表。
通过LRU 、LRUW等链表来管理哪些块可以被重用(覆盖),以此来加快物理读加载数据块到buffer cache时查找可用内存块的速度。
在前面描述buffer cache结构的图上,也可以看到有两个链表:LRU和LRUW。
在介绍LRU和LRUW前,先说明几个概念。
1. 脏数据块(dirty buffer):buffer cache中的内存数据块的内容与数据文件中的数据块的内容不一致。
2. 可用数据块(free buffer):buffer cache中的内存数据块为空或者其内容与数据文件中的一致。注意,可用数据块不一定是空的。
3. 钉住的数据块(ping buffer):当前正在更新的内存数据块。
4. 数据库写进程(DBWR):这是一个很底层的数据库后台进程。既然是后台进程,就表示该进程是不能被用户调用的。由oracle内置的一些事件根据需要启动该进程,该进程用来将脏数据块写入磁盘上的数据文件。 LRU表示Least Recently Used,也就是指最近最少使用的buffer header链表。LRU链表串连起来的buffer header都指向可用数据块。而LRUW则表示Least Recently Used Write,也叫做dirty list,也就是脏数据块链表,LRUW串起来的都是修改过但是还没有写入数据文件的内存数据块所对应的buffer header。某个buffer header要么挂在LRU上,要么挂在LRUW不能同时挂在这两个链表上。
LRU表示Least Recently Used,也就是指最近最少使用的buffer header链表。LRU链表串连起来的buffer header都指向可用数据块。
而
LRUW则表示Least Recently Used Write,也叫做dirty list,也就是脏数据块链表,LRUW串起来的都是修改过但是还没有写入数据文件的内存数据块所对应的buffer header。
某个buffer header要么挂在LRU上,要么挂在LRUW上,不能同时挂在这两个链表上。
#########################################################################################
2.关于X$BH中lru_flag不同标志位的意义
SYS@ bys3>select distinct lru_flag from x$bh; --LRU_FLAG有这5种标记位。
LRU_FLAG
----------
6
2
4
8
0
X$BH.lru_flag 为0,对应在产LRU的冷端头--DSI中有。也可以通过DUMP buffer cache,搜索BH中LRU的值来计算0状态时BH的位置--很复杂啊哈哈。
X$BH.lru_flag 为2,对应:lru-flags: moved_to_tail 在LRU热端
X$BH.lru_flag 为8,对应:lru-flags: hot_buffer 在LRU热端 ---据说还有一种状态是9,也是在热端。
X$BH.lru_flag 为4,对应:lru-flags: on_auxiliary_list
X$BH.lru_flag 为6,对应:lru-flags: moved_to_tail on_auxiliary_list 在辅助LRU
--可以通过查出每种标志位的文件号块号并做DUMP来验证,除了0无法DUMP BH验证,其它都可以。
方法:
SYS@ bys3>select DBARFIL,DBABLK from x$bh where lru_flag=2 and rownum=1;
DBARFIL DBABLK
---------- ----------
2 15796
SYS@ bys3>alter system dump datafile 2 block 15796; --10G不能用这命令,11G可以。
3.LRU链表的管理机制:
8i前的LRU管理机制不再讨论。
8i以后,LRU和LRUW链表都具有两个子链表,分别叫做辅助链表和主链表。
同时对buffer header增加了一个属性:touch数量,也就是每个buffer header曾经被访问过的次数,来对LRU链表进行管理。--x$bh.tch
oracle每访问一次buffer header,就会将该buffer header上的touch数量增加1,因此,touch数量“近似”的体现了某个内存数据块总共被访问的次数。注意,这只是近似,并不精确。因为 touch的增加并没有使用latch来管理并发性。这只是一个大概值,表示趋势的,不用百分百的精确(3秒内的多次对块访问只计一次)。
############################
4.块在LRU上移动时TCH的变化:
1.大表全表扫描只使用辅助LRU,此时TCH=1,如此后再有对大表中某一行的读取,TCH=2。此时如果SMON进程醒来,或者遇到有其它物理读扫描到些块,会将此TCH=2的块移到主LRU热端头,lru_flag=8,TCH清零。
2.正常读取时,在辅助LRU上找到可用块后会将其移动到主LRU的冷端头,lru_flag=0,tch=1
3.主LRU的热端尾的块被挤出到冷端头时,TCH不变。
4.主LRU冷端最尾的块被挤出时,如TCH>=2时,也会被移动到热端头,TCH=0;
5.flush buffer_cache后查询,块都是TCH=0。SMON进程移动主LRU上TCH<2块到辅助LRU上时不清零。
总结下就是:只有移动到热端头时TCH会清零,flush buffer_cache会将所有块的TCH清零.
主LRU与辅助LRU上块的比例大致是:75%,25%。
在数据库刚启动或者刚flush buffer_cache后所有buffer都在辅助LRU上。SMON进程每3秒会从主LRU冷端尾开始扫描,将TCH<2的块移动到辅助LRU,以此维护主LRU与辅助LRU上块的比例。
主LRU上冷端和热端上块的比例是50%,这由隐含参数:_db_percent_hot_default决定的--默认值就是 50
KEEP POOL/RECYCLE POOL里没有冷热端之分。
_db_percent_hot_keep和_db_percent_hot_recycle默认是0,表示数据块进入KEEP POOL/RECYCLE POOL时放在LRU列尾端。
结合后面物理读查找可用buffer 的过程 ,来说明一下LRU与LRUW的原理: 可以参考:点击打开链接
4.物理读的过程,也是在buffer cache中LRU链表上查找挂载可用块的过程,其中也涉及的DBWR写LRUW上脏块的操作,例如:
注:这里的读取是非直接路径读,非大表的全表扫描。关于直接路径读与大表的全扫描,见:点击打开链接
6.如果未查找到所需BH,将发生物理读。服务器进程将从磁盘上的相应数据文件中读取所需块,并将此块读入buffer cche中。
7.将块读入buffer cache中时,如何找到一个可以使用的buffer呢?下面步骤进行一步步解析。
8.首先在辅助LRU的最尾端向前查找可用buffer,TCH<2的块可以被重用。
9.如果辅助LRU最尾端的块是TCH<2的块,则将直接使用此块,并将其移动到主LRU的冷端头。
同时也会根据此块的DBA进行HASH,查找相应的HASH BUCKET,将此块加入到对应的HASH CHAIN上,并对BH中的相应信息进行修改(如对应X$BH中的LRU_FLAG,NXT_HASH、BA等字段的具体值)--此过程也需要相应的CBC LATCH /buffer pin锁的获取释放等。
再把数据块的值返回前台进程,此时物理读就完成了。
10.如果辅助LRU最尾端的块是TCH>=2的块,则首先将此块移动到主LRU的热端头,
同时TCH清零;然后在在辅助LRU上继续向前查找,直到找到可用的块---TCH<2。
之后的过程和步骤9中的就一样了。
(SMON每三秒时,服务器进程扫描空闲BUFFER时;都会把辅助LRU中TCH大于等于2的移到主的热端头)
11.如果在辅助LRU上搜索完毕扔未找到可以使用的块,则将从主LRU的冷端尾开始搜索。
12.如果主LRU最尾端的块是TCH<2的块,则将直接使用此块,并将其移动到主LRU的冷端头,
TCH为1。如是TCH>=2的块,则将其移动到热端头,TCH清零。如果是脏块,则将其移动到主LRUW上。依此规则向前搜索查找可用块。(
SMON每3秒,从主LRU冷端查找TCH小于2的非脏块到辅助LRU确保辅助LRU中有可用BUFFER)
13.如果从主LRU最尾端向前搜索了40%(隐含参数_db_block_max_scan_pct,
11GR2后好像不太一样了?)还未找到可用块,则将触发DBWR写LRUW上的脏块--(CKPTQ队列的写不涉及LRUW,
只有DBWR会写LRUW上脏块,并且写的是LRUW上的全部脏块-每三秒醒来也要全部写出LRUM上所有块才会休眠。写LRUW上脏块的步骤是:DBWR进程写时或者SMON进程每三秒醒来时(LRUW进程不像辅助LRUW那样,非DBWR进程也允许访问),会将主LRUW上的一部分脏块移动到辅助LRUW,然后在辅助LRUW上排序、写入磁盘;然后再从主LRUW上移动下一批,直到写出完毕再次进入睡眠。并且在DBWR写出过程中,会产生
free buffer waits),写出后的buffer将重新挂载到辅助LRU上并变为可用。