|
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.
|