深入学习Buffer Cache

2008-04-25 04:18:36.0     推荐:0    收藏:0    评论:0     来源:e800数据库频道


Buffer Cache内部

Buffer cache是为了实现消除频繁的数据块物理I/O,从而提高并发访问和OLTP应用程序的性能。Oracle buffer cache能够:
数据在内存中可用:减少物理I/O
一致读:数据在内存相同的块可拥有多个版本
并发控制:通过利用各种类型的锁来控制并发访问同一资源
术语
data buffer header list的结构是用哈希桶Hash bukets来构建的,它按照分组。注意,block class和block type不同。

Hash chain,则是在每一个Hash buket内部,维护多个data buffer header的数据结构。

LRU则是一种用于寻找free buffer的机制。

(Rdba = relative data block address)

LRU和buckets/chains不同:
• 用于寻找某一指定的hash buckets/chains
• LRU提供寻找空闲buffer的方法,从list尾部开始找。
Buffer header内容可以通过访问X$BH表来访问。
Hash Buckets and Chains

默认的hash buckets数量 = DB_BLCOK_BUFFERS × 2

这个参数是指定buffer cache里面的块数,它×block size就是buffer cache的大小。但是在Oracle10g里面,内存默认是自动管理的,所以这个参数一般是0,而没有参考价值。

它可以通过内部参数_db_block_hash_buckets来重写,在我的测试系统中,改参数为:
_db_block_hash_buckets 131072 = 1G
每个Hash buckets里面包含一个data buffer header chain,按一组一组排列。
每个Hash buckets拥有一个cache buffer chain latch,用于控制并发访问。
如何对hash chain的访问都必须被cache buffer chain latch所保护。Hash chain本身更新是通过普通的链表机制。尽管每次访问都必须加latch,但是latch和buckets并不是1 to 1对应关系,默认的值为Initial = buffers / 128,如果它太低,可以调整。
转储缓冲区头:
Alter system set events ‘immediate trace name buffers level 1’;

我导出了一个28M的trace文件,实际就是一个一个的BH,内容节选如下:
BH (0x75bf6e40) file#: 5 rdba: 0x015ad936 (5/1759542) class: 1 ba: 0x75b36000
文件号5 块地址0x015ad936 类型 1
set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: 52661 objn: 52661 tsn: 6 afn: 5
hash: [7f1e4bd8,7f1e4bd8] lru: [70ff2ce0,79404190]
lru-flags: hot_buffer
ckptq: [NULL] fileq: [NULL] objq: [70ff2d48,6d3e46b8]
st: XCURRENT md: NULL tch: 0
flags: gotten_in_current_mode block_written_once redo_since_read
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
BH (0x767ed150) file#: 2 rdba: 0x00803c80 (2/15488) class: 38 ba: 0x765f2000
set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: -1 objn: 0 tsn: 1 afn: 2
hash: [7f1e4c08,7f1e4c08] lru: [6d3e0c30,6a7f9a58]
ckptq: [6a7f9a08,6cbfa3b8] fileq: [6a7f9a18,6d3e0bf0] objq: [6d3e0c98,6a7f9ac0]
st: XCURRENT md: NULL tch: 1
flags: buffer_dirty gotten_in_current_mode redo_since_read
LRBA: [0x5a1.9138.0] HSCN: [0x0.17e0c7d] HSUB: [4]
。。。
BH就是buffer header,文件中共有48723个BH,说明我的buffer header list共维护48723个buffer块的信息。里面标志可以参考DSI来了解。
Overview of the Buffer Cache

buffer header包含如下信息:
• Buffer在内存中的基本地址
• hash chain的信息
• others.它在链表中的位置,诸如LRU,LRUW,CKPTQ和其它。
• 当前用户和等待者信息
• 各种动作和状态的标志
• 各种CR区域,诸如redo rba,为实现多种操作
Buffer descriptor存在于PGA,它实际属于client。它存储的消息是client提供的。
Buffer Header

• 描述缓冲区的buffer
• 在TSN, RDBA 做hash算法,为hash buckets获取一个固定的数字
• 被上层组件做为一种提示使用
Buffers的状态包括:
• FREE: 可被重用
• READING: 正在从磁盘读取到缓冲区
• EXLCUR: 当前buffer
• SHRCUR: 当前buffer
• IRECOVERY: Buffer在进行实例级恢复
• MRECOVERY: Buffer在进行介质恢复
Buffers能够被请求为NULL, NEW, CR, CRX, SHR, 和 EXL 模式。
Multiple Buffer Pools

Buffer pool 有三种类型:

Default:默认的池

Recycle:大对象池,当数据库读取某一大对象时候,可能导致其它对象的buffer老化,所以可以把大对象的读取指定到recycle池中,避免影响其它对象。这个池里默认为CR块。

Keep:和recycle相反,把常用对象的buffer长久keep在池中。 通过DB_KEEP_CACHE_SIZE(BUFFER_POOL_KEEP)参数可以指定大小。

SQL> create table test (n number) storage (buffer_pool keep);

SQL> alter table test storage (buffer_pool recycle);

公式:

Buffers in default pool = Total number of buffers – (Buffers in recycle pool + Buffers in keep pool)

可以通过V$SEGSTAT_NAME, V$SEGSTAT, 和 V$SEGMENT_STATISTICS这个三个视图进行段级别的监控。
LRU

Lru包含两个list:
LRUW:LRU Write list,也就是dirty list,维护着当前的脏块。
LUR:最近最少使用
一个buffer只能被其中一个list cache。
Multiple LRU lists

LRU list 由内部参数_db_block_lru_latches控制,我的环境下为 8。以前,只有一个list在cache里。从Oracle8开始,CKPTQ(checkpoint queues) 和FQ (file queues)被引入。随着异步DBWn引入,list的管理也随之变化。现在正确称呼,应该是“working sets,”,包含几个不同的LRU lists。每个LRU是受cache buffers lru chain latch,保护的。

LRU Lists

每一对LRU/LRUW称为一个working set,buffer通过循环使用的方式分配到这些working set中。在高负载的读取和过多的LRU扫描下,LRU latch可能成为一个瓶颈。此外,LRU latch在多路并发系统中也不好衡量。应为多个CPU可能都会请求单独的LRU latch。通过设置内部参数_DB_BLOCK_LRU_LATCHES,可以改善LRU的竞争情况。每个LRU latch都是一个working set。每个working set拥有自己的链表,管理属于它的buffer。如果一个latch不能访问,那么就会转向下一个,直到获得一个成功的latch。在事件管理中,如果请求latch失败,那么miss就会增加。LRU latch的请求最初是NOWAIT模式,如果在它请求完所有latch都仍然没有成功后,就会变为WAIT模式。

进程访问所有的latch直到它获得一个LRU latch。在多个DBWR的环境下,每个DBWR都有自己的LRU/LRUW,通过设置db_writer_processes可以更改DBWR的数目。

Working Sets
Working Sets = DBWRs x Maximum number of buffer pools (8).

它包含如下list:
LRU:
LRU-W:记录dirty buffer
LRU-P:Ping list(RAC)
LRU-XO::对象重用list,针对drop和truncate
LUR-XR:块重用list
Thread CKPT queue:线程CKPT buffer
File CKPT queue:文件CKPT buffer queue
Recovery CKPT queue:执行恢复的块
Buffer Get

请求一个buffer,首先会填充一个buffer descriptor,步骤如下:

• 填充一个buffer descriptor
• 指定buffer的占有模式
• 缓存数据块
典型的buffer descriptor保护如下信息:
• Full RDBA
• Class
• Object#
• 指向buffer header的提示
• Pin (buffer state object with which to associate it)
• Examination function (if CR or CRX)
• If multiblock read, then the number of buffers to be read
Getting Current Buffers

寻找current buffers:
1. Scan the hash chain to which the RDBA maps. 扫描hash chain,获取RDBA maps
2. 为每个buffer的chain:
- 忽略RDBA不匹配的buffer
- 等待READING buffer 然后返回它们
- 跳过CR (consistent read) buffers
- 如果CUR (current) buffer 处于compatible模式,则可使用
- 否则,如果是CR状态对象
–复制一个CR 然后 创建一个新的EXLCUR 副本
– 或者等待当前的buffer释放
- 如果没有找到buffer,则从硬盘读取
2. 关联state object到buffer header
3. 返回指向数据区域
Buffer Management

• Buffers初始化hash到 LRU-AUX list.

– Buffers等待被使用

– 位于LRU尾部的Buffers 被DBWR扫描,检验能否清除

• 一般首先扫描LRU-AUX.

• 分配空闲空间后,移动到MAIN list的中部.

• DBWR 扫描 LRU-MAIN 然后移动脏块到LRUW-MAIN.

write-out一个老化的buffer的流程如下:
1. Buffer最初是在LRU-AUX.
2. 移动到LRU-MAIN.
3. 被修改后(释放闩),移出LRUW-MAIN. DBWR稍后把它移动到LRUW-AUX.
4. 出列到I/O 槽 通过 kcbbxsv(Oracle内部程序模块名称).
5. Queue writes from I/O slots 通过 kcfqueuewr.
6. 写入disk 通过kcfdowr.
7. 完成


Touch 的数量

再看之前的dump buffer header,某一个bh其中的tch为50

BH (0x743edb00) file#: 6 rdba: 0x0182fcf1 (6/195825) class: 1 ba: 0x74206000

set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0

dbwrid: 0 obj: 52726 objn: 52726 tsn: 7 afn: 6

hash: [7f217f60,6d7e7070] lru: [723ea068,6afe2d20]

ckptq: [69ffa798,72ff67a8] fileq: [747e5498,6ebec9d0] objq: [733de6d0,677f7308]

st: XCURRENT md: NULL tch: 50

flags: buffer_dirty gotten_in_current_mode block_written_once

redo_since_read

LRBA: [0x5a1.9bc8.0] HSCN: [0x0.17e16b4] HSUB: [2]

也可以通过如下sql查询某个buffer的touch数

SELECT TCH FROM SYS.X$BH WHERE FILE#=5 AND DBABLK=195825;

当一个buffer被touch到缓存中,touch数量就会增加1。
Buffer Cache Fixed Tables

• X$KCBBF: Buffer descriptor 状态对象和标志
• X$KCBWH: 描述where/why buffer gets的统计
• X$KCBSW: why/where buffer gets的统计. Only WHY0 is used.
• X$KCBBMC: Map count per buffer when _DB_BLOCK_CACHE_MAP is TRUE
• X$KCBBHS: DBWR histogram statistics, enabled if _DB_WRITER_HISTOGRAM_STATISTICS is TRUE
• X$KCBBES: DBWR event statistics.
• X$KCBWDS: Working set descriptor.
• V$WAITSTAT on X$KCBWAIT
• V$BUFFER_POOL on X$KCBWBPD
Buffer Cache Latches

Cache buffers chains

前面已经介绍过,块被hash为一个链,基于一对。每一个chain被一个子cache buffers chains保护,防止在扫描过程被改变。
Cache buffers LRU chain

保护LRU链。进程需要获得latch,当他们需要移动buffer到某个LUR set的时候。在Oracle server里面,它是最繁忙latch之一。为了减少竞争,可以设置多个_DB_BLOCK_LRU_LATCHES
Checkpoint queue latch

在进程把buffer放在checkpoint queue之前,它必须确保queue没有正在被使用。这有点和LRUlist 类似。


Buffer Cache调优

调整buffer cache 包含 I/O和DBWR调优

• 较好的缓存命中率(一般> 90%).

• 消除使用缓冲区高速缓存只要有可能.

• 使用直接I/O (direct loader, direct insert).

– SORT_MULTIBLOCK_READ_COUNT

通过以下SQL可以获得对buffer cache size的建议

SELECT size_for_estimate,buffers_for_estimate,estd_physical_read_factor,estd_physical_reads
FROM v$db_cache_advice t
WHERE NAME=''DEFAULT'' AND advice_status=''ON'' AND block_size=(SELECT Value FROM v$parameter WHERE NAME=''db_block_size'');

• 有两个主要的事件导致大量的wait time和性能瓶颈:

– Buffer busy waits

– Free buffer waits

• 一般首先调整最高的等待事件.

select *
from v$system_event
order by time_waited DESC

Buffer Busy Waits

– 多个会话读取相同的块

– 多个会话等待改变相同的块

P3 字段在V$SESSION_WAIT 是原因code.

V$WAITSTAT 显示每种类型的块的wait.

X$KCBFWAIT 显示每个文件的wait

建议的方法:

• 查询V$WAITSTAT 观察waits 的走势.

• 查询 X$KCBFWAIT 发现最忙的文件.

• 联合结果发现是哪个对象忙碌.

SELECT count, file#, name
FROM x$kcbfwait, v$datafile
WHERE indx + 1 = file#
ORDER BY count

最有可能发生等待的块类型:

段 Header

Problem: 没有足够的free lists 和大量的inserts(10g ASSM有所改进)
Solution: 增加更多的free lists 和 free lists 组
Problem: HWM 经常变化
Solution: 增大 _BUMP_HIGHWATER_MARK_COUNT
Problem: Extent size太小, 表增加太快
Solution: 增加 extent sizes

Data Blocks

Problem: 每个block行太多

Solution 1: 减少rows,通过改变pctfree/pctused

Solution 2: 减小 DB_BLOCK_SIZE

Problem: Right-hand indexes 导致 插入相同的block
Solution: 使用反向key indexes 或者重建 index

Free Buffer Waits

• 什么时候发生:

当移动脏buffer过去的时候,LRUW list 满了

DBWR 不能清除LRUW 足够快

• 解决方法:

增加批量写的大小

条带化磁盘, 使得DBWR 更快

使用异步 I/O

减少LRU lists数量(做为最后办法,可能导致latch竞争,因为没有足够的latch)

初始化参数

下面是影响buffer cache的内部参数。

• _DB_BLOCK_HASH_BUCKETS: block hash buckets的数量 (默认是DB_CACHE_SIZE/4)

• _DB_BLOCK_MAX_SCAN_CNT: 寻找空闲buffer的最大尝试次数

• _DB_WRITER_SCAN_DEPTH: DBWR寻找脏块的的扫描深度

• _DB_BLOCK_CACHE_CLONE: 取的时候,复制一个副本 (调试用)

• _DB_BLOCK_CACHE_MAP: Map/unmap and track reference counts on blocks (for debugging purposes only)

• _DB_BLOCK_MAX_CR_DBA: Maximum allowed number of CR buffers per dba

• _DB_WRITER_HISTOGRAM_STATISTICS: Maintain DBWR histogram statistics in X$KCBBHS

• _DB_BLOCK_MED_PRIORITY_BATCH_SIZE: Percentage of write batch used for medium priority checkpoints

• _DB_BLOCK_HI_PRIORITY_BATCH_SIZE: Percentage of write batch used for high priority checkpoints

• _DB_PERCENT_HOT_DEFAULT: Percent of default buffer pool considered hot

• _DB_PERCENT_HOT_KEEP: Percent of keep buffer pool considered hot

• _DB_PERCENT_HOT_RECYCLE: Percent of recycle buffer pool considered hot

• _DB_AGING_HOT_CRITERIA: Touch count which sends a buffer to head of replacement list

• _DB_AGING_COOL_COUNT: Touch count set when buffer cooled

• _DB_AGING_TOUCH_TIME: Touch count which sends a buffer to head of replacement list

• _DB_AGING_FREEZE_CR: Make CR buffers always be too cold to keep in cache

• _DB_AGING_STAY_COUNT: 当buffer移动到LURheader的Touch count

• _DB_BLOCK_CACHE_PROTECT: 保护 database blocks (调试用)

• _DB_BLOCK_HASH_LATCHES: database block hash latches的数目

• _DB_HANDLES: 同时能发生的buffer操作,系统级

• _DB_HANDLES_CACHED: 每个进程缓存的Buffer 句柄

• _DB_LARGE_DIRTY_QUEUE: 脏块队列强制写入磁盘的门限

• _DB_WRITER_MAX_WRITES: 最大的DB Writer I/Os

• _DB_WRITER_CHUNK_WRITES: DBWR 尝试写等待次数

• _DBWR_ASYNC_IO: 激活 DBWR 异步写

• _DBWR_TRACING: 激活 DBWR 追踪

• _DBWR_SCAN_INTERVAL: DBWR scan interval

• _TRACE_BUFFER_FLUSHES: 追踪buffer flushes 如果otrace cacheIO事件被设置

• _TRACE_MULTI_BLOCK_READS: 追踪 multi-block 读,如果otrace cacheIO事件被设置

• _TRACE_CR_BUFFER_CREATES: 追踪 CR buffer 创建,如果 otrace cacheIO 事件被设置

其它技巧

• 使用多个LRU latch的时候,使用多个DBWR进程。

• 尽量使LRU latch丢失率小于1%

• 使用asynchronous I/O 或者 DBWR I/O Slaves.

• 减少 buffer cache load 通过使用direct I/O.



您可以针对本文进行:[评论]  [收藏]  [推荐]   [查看原文链接]  
  • 共有0条评论  点击查看更多评论
  • 网友评论仅供网友表达个人看法,并不表明e800同意其观点或证实其描述
我想发表评论:
用户名密码
  • 匿名发表
    验证码: