Organization of the Database Buffer Cache
The buffers in the cache are organized in two lists: the write list and the least recently used (LRU) list.The write list holds dirty buffers, which contain data that has been modified but has not yet been written to disk.The LRU list holds free buffers, pinned buffers, and dirty buffers that have not yet been moved to the write list.Free buffers do not contain any useful data and are available for use. Pinned buffers are currently being accessed.
When an Oracle Database process accesses a buffer, the process moves the buffer to the most recently used (MRU) end of the LRU list. As more buffers are continually moved to the MRU end of the LRU list, dirty buffers age toward the LRU end of the LRU list.
The first time an Oracle Database user process requires a particular piece of data, it searches for the data in the database buffer cache.If the process finds the data already in the cache (a cache hit), it can read the data directly from memory.If the process cannot find the data in the cache (a cache miss), it must copy the data block from a datafile on disk into a buffer in the cache before accessing the data.Accessing data through a cache hit is faster than data access through a cache miss.
Before reading a data block into the cache, the process must first find a free buffer. The process searches the LRU list, starting at the least recently used end of the list. The process searches either until it finds a free buffer or until it has searched the threshold limit of buffers.
If the user process finds a dirty buffer as it searches the LRU list, it moves that buffer to the write list and continues to search. When the process finds a free buffer, it reads the data block from disk into the buffer and moves the buffer to the MRU end of the LRU list.
If an Oracle Database user process searches the threshold limit of buffers without finding a free buffer, the process stops searching the LRU list andsignals the DBW0 background process to write some of the dirty buffers to disk.
MRU and LRU blocks
Blocks within the buffer cache are ordered from MRU (most recently used) blocks to LRU (least recently used) blocks. Whenever a block is accessed, the block goes to the MRU end of the list, thereby shifting the other blocks down towards the LRU end. When a block is read from disk and when there is no buffer available in the db buffer cache, one block in the buffer cache has to “leave”. It will be the block on the LRU end in the list.
However, blocks read during a full table (multi block reads are placed on the LRU side of the list instead of on the MRU side.
The v$bh dynamic view has an entry for each block in the buffer cache. The time a block has been touched most recently is recorded in tim ofx$bh
下图就是LRU List的结构示意图
Touch count
Each buffer has an associated touch count. This touch count might be increased if a buffer is accessed (although it needs not always be). It is valid to claim that the higher the touch count, the more important (more used) the buffer. Therefore, buffers with a high touch count should stay in the buffer cache while buffers with a low touch count should age out in order to make room for other buffers.
A touch time can only be increased once within a time period controlled by the parameter_db_aging_touch_time (default: 3 seconds).
The touch count is recorded in the tch column of x$bh.
By the way, it looks like Oracle doesn’t protect manipulations of the touch count in a buffer with alatch. This is interesting because all other manipulations on the LRU list are protected by latches. A side effect of the lack of latch-protection is that the touch count is not incremented if another process updates the buffer header.
Buffer Cache概述
Buffer Cache是SGA的一部分,Oracle利用Buffer Cache来管理data block,Buffer Cache的最终目的就是尽可能的减少磁盘I/O。Buffer Cache中主要有3大结构用来管理Buffer Cache。
Hash Bucket & Hash Chain List :Hash Bucket与Hash Chain List用来实现data block的快速定位。
LRU List :挂载有指向具体的free buffer, pinned buffer以及还没有被移动到 write list的dirty buffer 等信息。所谓的free buffer就是指没有包含任何数据的buffer,所谓的pinned buffer,就是指当前正在被访问的buffer。
Write(Dirty)List :挂载有指向具体的 dirty block的信息。所谓的dirty block,就是指在 buffer cache中被修改过但是还没有被写入到磁盘的block。
Hash Bucket
和 Hash Chain List
Oracle将buffer cache中所有的buffer通过一个内部的Hash算法运算之后,将这些buffer放到不同的 Hash Bucket中。每一个Hash Bucket中都有一个Hash Chain List,通过这个list,将这个Bucket中的block串联起来。
要查看Hash Chain List组成, 可以通过x$bh字典.
SQL> desc x$bh
Name Null? Type
———————– – —————-
ADDR RAW(8) —block在buffer cache中的address
…
HLADDR RAW(8) –latch:cache buffers chains 的address
…
NXT_HASH RAW(8) —指向同一个Hash Chain List的下一个block
PRV_HASH RAW(8) —指向同一个Hash Chain List的上一个block
NXT_REPL RAW(8)—指向LRU list中的下一个block
PRV_REPL RAW(8)—指向LRU list中的上一个block
…..
Hash Chain List就是由x$bh中的NXT_HASH,PRV_HASH这2个指针构成了一个双向链表,其示意图如下:
通过NXT_HASH,PRV_HASH这2个指针,那么在同一个Hash Chain List的block就串联起来了。
理解了Hash Bucket 和 Hash Chain List,我们现在来看看Hash Bucket与 Hash Chain List管理Buffer Cache的结构示意图
这个图和Shared Pool 有点类似。从图中我们可以看到,一个latch:cache buffers chains(x$bh.hladdr) 可以保护多个Hash Bucket,也就是说,如果我要访问某个block,我首先要获得这个latch,一个Hash Bucket对应一个Hash Chain List,而这个Hash Chain List挂载了一个或者多个Buffer Header。
LRU List与LRU Write List
前面已经提到过了,如果一个用户进程发现某个block不在Buffer Cache中,那么用户进程就会从磁盘上将这个block读入Buffer Cache。在将block读入到Buffer Cache之前,首先要在LRU list上寻找Free的buffer,在寻找过程中,如果发现了Dirty Buffer就将其移动到 LRU Write List上。如果Dirty Queue超过了阀值25%(如下面查询所示),那么DBWn就会将Dirty Buffer写入到磁盘中。
SQL> select kvittag,kvitval,kvitdsc from x$kvit where kvittag in(‘kcbldq’,’kcbfsp’);
KVITTAG KVITVAL KVITDSC
——————– ———- ——————————————————-
kcbldq 25 large dirty queue if kcbclw reaches this
kcbfsp 40 Max percentage of LRU list foreground can scan for free
根据上面的查询我们还知道,当某个用户进程扫描LRU list超过40%都还没找到Free Buffer,那么这个时候用户进程将停止扫描LRU list,同时通知DBWn将Dirty Buffer写入磁盘,用户进程也将记录一个free buffer wait等待事件。如果我们经常看到free buffer wait等待事件,那么我们就应该考虑加大Buffer Cache了。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/aiops/7263.html