日历

2008 7.6 Sun
  12345
6789101112
13141516171819
20212223242526
2728293031  
«» 2008 - 7 «»

文章搜索

日志文章

2008年04月09日 15:10:50

深入学习Buffer Cache

Buffer Cache内部

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

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

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

(Rdba = relative data block address)

LRUbuckets/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,按一组一组<rdba,class>排列。
每个Hash buckets拥有一个cache buffer chain latch,用于控制并发访问。
如何对hash chain的访问都必须被cache buffer chain latch所保护。Hash chain本身更新是通过普通的链表机制。尽管每次访问都必须加latch,但是latchbuckets并不是1 to 1对应关系,默认的值为Initial = buffers / 128如果它太低,可以调整。
转储缓冲区头:
Alter system set events ‘immediate trace name buffers level 1’;

我导出了一个28Mtrace文件,实际就是一个一个的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,文件中共有48723BH,说明我的buffer header list共维护48723buffer块的信息。里面标志可以参考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块。

Keeprecycle相反,把常用对象的buffer长久keep在池中。 通过DB_KEEP_CACHE_SIZEBUFFER_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
LRUWLRU Write list,也就是dirty list,维护着当前的脏块。
LUR:最近最少使用
一个buffer只能被其中一个list cache
Multiple LRU lists

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

LRU Lists

每一对LRU/LRUW称为一个working setbuffer通过循环使用的方式分配到这些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-PPing listRAC
LRU-XO::对象重用list,针对droptruncate
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.         为每个bufferchain
-           忽略RDBA不匹配的buffer
-           等待READING buffer 然后返回它们
-           跳过CR (consistent read) buffers
-           如果CUR (current) buffer 处于compatible模式,则可使用
-           否则,如果是CR状态对象
                      –复制一个CR 然后 创建一个新的EXLCUR 副本
                      – 或者等待当前的buffer释放
-                               如果没有找到buffer,则从硬盘读取
2.         关联state objectbuffer 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 通过 kcbbxsvOracle内部程序模块名称).
5.   Queue writes from I/O slots 通过 kcfqueuewr.
6.   写入disk 通过kcfdowr.
7.   完成


Touch 的数量

再看之前的dump buffer header,某一个bh其中的tch50

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查询某个buffertouch

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

当一个buffertouch到缓存中,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为一个链,基于一对<DBA,class>。每一个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/ODBWR调优

      较好的缓存命中率(一般> 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 和大量的inserts10g 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移动到LURheaderTouch 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.



Tags: buffer   oracle  

类别: Administer |  评论(0) |  浏览(3724) |  收藏
发表评论