buffer cache 内存结构深入分析!

来源:互联网 发布:股票模拟软件 编辑:程序博客网 时间:2024/05/16 07:31

1. buffer cache的概念

buffer cache——为了能够加快处理数据的速度,oracle必须将读取过的数据缓存在内存里。而oracle对这些缓存在内存里的数据起了个名字:数据高速缓存区(db buffer cache),通常就叫做buffer cache。

oracle内部在实现其管理的过程中,有两个非常有名的名词:链表和hash算法。

链表是一种数据结构,通过将对象串连在一起,从而构成链表结构。这样,如果要修改、删除、查找某个对象的话,都可以先到链表中去查找,而不必实际的访问物理介质。oracle中最有名的链表大概就是LRU链表了,我们后面会介绍它。

hash算法则是为了能够进行快速查找定位所使用一种技术。所谓hash算法,就是根据要查找的值,对该值进行一定的hash算法后得出该值所在的索引号,然后进入到该值应该存在的一列数值列表(可以理解为一个二维数组)里,通过该索引号去找它应该属于哪一个列表。然后再进入所确定的列表里,对其中所含有的值,进行一个一个的比较,从而找到该值。这样就避免了对整个数值列表进行扫描才能找到该值,这种全扫描的方式显然要比hash查找方式低效很多。其中,每个索引号对应的数值列在oracle里都叫做一个hash bucket。

列举一个最简单的hash算法。假设我们的数值列表最多可以有10个元素,也就是有10个hash buckets,每个元素最多可以包含20个数值。则对应的二维数组就是t[10][20]。我们可以定义hash算法为n MOD 10。通过这种算法,可以将所有进入的数据均匀放在10个hash bucket里面,hash bucket编号从0到9。比如,我们把1到100都通过这个hash函数均匀放到这10个hash bucket里,当查找32在哪里时,只要将32 MOD 10等于2,这样就知道可以到2号hash bucket里去找,也就是到t[2][20]里去找,2号hash bucket里有10个数值,逐个比较2号hash bucket里是否存在32就可以了。

hash bucket——就是我们前面说明hash算法中提到的二维数组的第一维。它是通过对buffer header 里记录的数据块地址和数据块类型运用hash算法以后,得到的组号。每个hash bucket都是通过不同的hash chain而体现出来的。

buffer header——每一个数据块在被读入buffer cache时,都会先在buffer cache中构造一个buffer header,buffer header与数据块一一对应。

hash chain——属于同一个hash bucket的所有buffer header所串起来的链表。


buffer header包含的主要信息有:

1) 该数据块在buffer cache中实际的内存地址。就是上图中的虚线箭头所表示的意思。

2) 该数据块的类型,包括data、segment header、undo header、undo block等等。

3) 该buffer header所在的hash chain,是通过在buffer header里保存指向前一个buffer header的指针和指向后一个buffer header的指针的方式实现的。

4) 该buffer header所在的LRU、LRUW、CKPTQ等链表(这些链表我们后面都会详细说明)。也是通过记录前后buffer header指针的方式实现。

5) 当前该buffer header所对应的数据块的状态以及标记。

6) 该buffer header被访问(touch)的次数。

7) 正在等待该buffer header的进程列表(waiter list)和正在使用该buffer header的进程列表(user list)。


先说明几个概念:

1) 脏数据块(dirty buffer):buffer cache中的内存数据块的内容与数据文件中的数据块的内容不一致。

2) 可用数据块(free buffer):buffer cache中的内存数据块为空或者其内容与数据文件中的一致。注意,可用数据块不一定是空的。

3) 钉住的数据块(ping buffer):当前正在更新的内存数据块。

4) 数据库写进程(DBWR):这是一个很底层的数据库后台进程。既然是后台进程,就表示该进程是不能被用户调用的。由oracle内置的一些事件根据需要启动该进程,该进程用来将脏数据块写到磁盘上的数据文件。

lru——LRU表示Least Recently Used,也就是指最近最少使用的buffer header链表。LRU链表串连起来的buffer header都指向可用数据块。

lruw()——LRUW则表示Least Recently Used Write,也叫做dirty list,也就是脏数据块链表,LRUW串起来的都是修改过但是还没有写入数据文件的内存数据块所对应的buffer header。


2. 转储buffer cache

就象实例中的其他内存结构一样,oracle提供了可以将buffer cache转储到跟踪文件的方法。方法如下:

ALTER SESSION SET EVENTS 'immediate trace name buffers level <level>';
这里的level有很多值,分别可以转储buffer cache中的不同的内容。level的可选值包括:

1 只转储buffer header

2 在level 1的基础上再转储数据块头

3 在level 2的基础上再转储数据块内容

4 转储buffer header和hash chain

5 在level 1的基础上再转储数据块头和hash chain

6 在level 2的基础上再转储数据块内容和hash chain

8 转储buffer header和hash chain以及users/waiters链表

9 在level 1的基础上再转储数据块头、hash chain以及users/waiters链表

10 在level 2的基础上再转储数据块内容、hash chain以及users/waiters链表

我们创建一个简单的测试表,然后看看转储出来的buffer header是什么样子的。

SQL> create table buffer_test(id number);表已创建。SQL> select object_id from dba_objects where object_name='BUFFER_TEST'; OBJECT_ID----------     73701SQL> insert into buffer_test values(1);已创建 1 行。SQL> commit;提交完成。
这时我们知道buffer_test表的object_id是73701,同时,该表中只有2个block具有数据。1个是segment header,另一个就是实际存放了1这个值的数据块。接着我们把buffer header转储出来:
SQL> ALTER SESSION SET EVENTS 'immediate trace name buffers level 1';会话已更改。SQL> select d.value || '/' || lower(rtrim(i.instance, chr(0))) || '_ora_' ||       --获取跟踪文件       p.spid || '.trc' trace_file_name  3    from (select p.spid  4           where m.statistic# = 1sys.v$session s, sys.v$process p          from sys.v$mystat m, sys.v$session s, sys.v$process p  5           where m.statistic# = 1  6             and s.sid = m.sid  7             and p.addr = s.paddr) p,  8         (select t.instance  9            from sys.v$thread t, sys.v$parameter v 10           where v.name = 'thread' 11             and (v.value = 0 or t.thread# = to_number(v.value))) i, 12         (select value from sys.v$parameter where name = 'user_dump_dest') d;TRACE_FILE_NAME--------------------------------------------------------------------------------/u01/app/diag/rdbms/orcl/orcl/trace/orcl_ora_3817.trc
user_dump_dest所定义的目录下,找到跟踪文件并打开,可以看到类似下面的信息,这里我们列出前两个buffer header以及我们建立的object_id为73701的buffer_test表所对应的buffer header的内容:
BH (0x373f3b44) file#: 1 rdba: 0x00415116 (1/86294) class: 1 ba: 0x372ae000  set: 5 pool 3 bsz: 8192 bsi: 0 sflg: 3 pwc: 0,15  dbwrid: 0 obj: 420 objn: 422 tsn: 0 afn: 1 hint: f  hash: [0x397f4334,0x51bf8b14] lru: [0x43bf9634,0x3abf32d4]  ckptq: [NULL] fileq: [NULL] objq: [0x41bf0cd4,0x36bf0210]  st: XCURRENT md: NULL tch: 1  flags:  LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]  cr pin refcnt: 0 sh pin refcnt: 0`````````````````````````````````BH (0x45ff59e8) file#: 1 rdba: 0x00415116 (1/86294) class: 1 ba: 0x45ef8000  set: 6 pool 3 bsz: 8192 bsi: 0 sflg: 3 pwc: 0,15  dbwrid: 0 obj: 420 objn: 422 tsn: 0 afn: 1 hint: f  hash: [0x51bf8b14,0x397f4334] lru: [0x397f4364,0x46beb770]  lru-flags: moved_to_tail on_auxiliary_list  ckptq: [NULL] fileq: [NULL] objq: [NULL]  st: FREE md: NULL tch: 0 lfb: 33  flags:  cr pin refcnt: 0 sh pin refcnt: 0``````````````````````````````````````````````````````````````````````BH (0x39feb6c4) file#: 1 rdba: 0x00414df1 (1/85489) class: 1 ba: 0x39d6e000  set: 5 pool 3 bsz: 8192 bsi: 0 sflg: 3 pwc: 0,15  dbwrid: 0 obj: 73701 objn: 73701 tsn: 0 afn: 1 hint: f  hash: [0x51b36c64,0x51b36c64] lru: [0x3ebf9b2c,0x51473c30]  obj-flags: object_ckpt_list  ckptq: [0x51476178,0x36fe9b70] fileq: [0x5147618c,0x36fe9b78] objq: [0x4e8799b8,0x4e8799b8]  st: XCURRENT md: NULL tch: 1  flags: buffer_dirty redo_since_read  LRBA: [0xa0.cd9e.0] LSCN: [0x0.16c3ed] HSCN: [0x0.16c3ed] HSUB: [1]  cr pin refcnt: 0 sh pin refcnt: 0`````````````````````````````````BH (0x3d7f9588) file#: 1 rdba: 0x00414df0 (1/85488) class: 4 ba: 0x3d788000  set: 6 pool 3 bsz: 8192 bsi: 0 sflg: 3 pwc: 0,15  dbwrid: 0 obj: 73701 objn: 73701 tsn: 0 afn: 1 hint: f  hash: [0x51bc78b4,0x51bc78b4] lru: [0x447f4ad8,0x3efefdd4]  obj-flags: object_ckpt_list  ckptq: [0x36beada8,0x363ed710] fileq: [0x3fbe63fc,0x5147a41c] objq: [0x4e8dc24c,0x4e8dc24c]  st: XCURRENT md: NULL tch: 2  flags: buffer_dirty redo_since_read  LRBA: [0xa0.cd82.0] LSCN: [0x0.16c3d5] HSCN: [0x0.16c3ed] HSUB: [1]  cr pin refcnt: 0 sh pin refcnt: 0
我们可以看到第一个BH (0x373f3b44)的hash: [0x397f4334,0x51bf8b14]和第二个BH (0x45ff59e8)的hash: [0x51bf8b14,0x397f4334]。这里记录的就是指向前一个buffer header和后一个buffer header的指针。这里,我们看到第一个BH所指向的后一个buffer header的指针是0x51bf8b14,而第二个BH所指向的前一个buffer header的指针也是0x51bf8b14,说明这两个buffer header是在同一个hash chain上。同样的,我们还可以看到类似结构的lru、ckptq、fileq,这些都是管理buffer header的一些链表结构。
然后,我们来看我们创建的buffer_test表所对应的buffer header。 首先,我们看到class,表示该buffer header所对应的数据块的类型,具体的值与含义的对应为:1=data block;2=sort block;3=save undo block;4=segment header;5=save undo header;6=free list;7=extent map;8=1st level bmb;9=2nd level bmb;10=3rd level bmb;11=bitmap block;12=bitmap index block;13=unused;14=undo header;15=undo block。我们可以看到与buffer_test表相关buffer header有两个:一个是4(segment header),另一个是1(data block)。

然后,我们看到rdba,这表示buffer header所对应的数据块的地址。我们可以看到class为1的buffer header的rdba为0x00414df1 (1/85489)。说明该数据块的位置是1号文件的85489号block里。004表示数据文件号乘以4,而14df1表示数据块的号。

SQL> select to_number('004','xxx')/4 as file#,to_number('14df1','xxxxx') as block# from dual;     FILE#     BLOCK#---------- ----------         1      85489
我们看到,该buffer header指向的就是1号文件里的85489号数据块。我们可以再来看看表buffer_test 里的rowid所告诉我们的文件号以及数据块号,从下面可以看到,结果是一样的。
SQL> select id,       dbms_rowid.rowid_relative_fno(rowid) as file#,       dbms_rowid.rowid_block_number(rowid) as block#  4    from buffer_test;        ID      FILE#     BLOCK#---------- ---------- ----------         1          1      85489
我们可以来看一下st,这表示buffer cache所指向的数据块的状态。一共有六种状态:FREE(0)=可以被重用的数据块;XCURRENT(1)=实例以排他方式获取的当前模式数据块;SCURRENT(2)=可以与其他实例共享的当前模式数据块;CR(3)=作为一致性读镜像的数据块,永远不会被写入磁盘;READING(4)=正在从磁盘读出的数据块;MRECOVERY(5)=正在进行介质恢复的数据块;IRECOVERY(6)=正在进行实例恢复的数据块。从状态说明中我们可以看到,现在表buffer_test的数据块都是当前模式的数据块。

另外,我们还可以看到tch,就是表示该数据块被扫描的次数。 以上这些是转储出来的内容。Oracle还提供了视图来显示buffer header的内容,这就是X$BH。这个视图就是把转储到平面文件以后所看到的诸如hash、st、tch等的值以列的方式呈现出来。这里就不做过多的介绍了,有兴趣的话,可以将该视图取出的结果与转储出来的文件进行比较,就可以知道每一列的含义。

SQL> desc x$bh; 名称                                      是否为空? 类型 ----------------------------------------- -------- ---------------------------- ADDR                                               RAW(4) INDX                                               NUMBER INST_ID                                            NUMBER HLADDR                                             RAW(4) BLSIZ                                              NUMBER NXT_HASH                                           RAW(4) PRV_HASH                                           RAW(4) NXT_REPL                                           RAW(4) PRV_REPL                                           RAW(4) FLAG                                               NUMBER FLAG2                                              NUMBER LOBID                                              NUMBER RFLAG                                              NUMBER SFLAG                                              NUMBER LRU_FLAG                                           NUMBER TS#                                                NUMBER FILE#                                              NUMBER DBARFIL                                            NUMBER DBABLK                                             NUMBER CLASS                                              NUMBER STATE                                              NUMBER MODE_HELD                                          NUMBER CHANGES                                            NUMBER CSTATE                                             NUMBER LE_ADDR                                            RAW(4) DIRTY_QUEUE                                        NUMBER SET_DS                                             RAW(4) OBJ                                                NUMBER BA                                                 RAW(4) CR_SCN_BAS                                         NUMBER CR_SCN_WRP                                         NUMBER CR_XID_USN                                         NUMBER CR_XID_SLT                                         NUMBER CR_XID_SQN                                         NUMBER CR_UBA_FIL                                         NUMBER CR_UBA_BLK                                         NUMBER CR_UBA_SEQ                                         NUMBER CR_UBA_REC                                         NUMBER CR_SFL                                             NUMBER CR_CLS_BAS                                         NUMBER CR_CLS_WRP                                         NUMBER LRBA_SEQ                                           NUMBER LRBA_BNO                                           NUMBER HSCN_BAS                                           NUMBER HSCN_WRP                                           NUMBER HSUB_SCN                                           NUMBER US_NXT                                             RAW(4) US_PRV                                             RAW(4) WA_NXT                                             RAW(4) WA_PRV                                             RAW(4) OQ_NXT                                             RAW(4) OQ_PRV                                             RAW(4) AQ_NXT                                             RAW(4) AQ_PRV                                             RAW(4) OBJ_FLAG                                           NUMBER TCH                                                NUMBER TIM                                                NUMBER CR_RFCNT                                           NUMBER SHR_RFCNT                                          NUMBER

3.buffer cache 优化

3.1.cache buffers chains latch

利用以下命令可以得到cache buffers chains latch的数量:

SQL> select count(*) from v$latch_children where name like 'cache buffers chains';  COUNT(*)----------      4096
还可以查看_db_block_hash_latches隐含参数得到相同的结果:
SQL> select ksppinm, ksppstvl from x$ksppi x, x$ksppcv y   2    where (x.indx = y.indx) and (translate(ksppinm, '_', '#')) like '_db_block_hash_latches';KSPPINM                        KSPPSTVL------------------------------ --------------------_db_block_hash_latches         4096
hash bucket数可以利用_db_block_hash_buckets隐含参数查看:
SQL> select ksppinm, ksppstvl from x$ksppi x, x$ksppcv y  2  where (x.indx = y.indx) and (translate(ksppinm, '_', '#')) like '_db_block_hash_buckets';KSPPINM                        KSPPSTVL------------------------------ --------------------_db_block_hash_buckets         131072
所以,一个cache buffers chains latch管理的bucket数就是:
SQL> select 131072/4096 from dual;131072/4096-----------         32
许多进程同时检索buffer cache时,获得cache buffers chains latch过程中发生争用,此过程中的等待事件是:
SQL> select name,parameter1,parameter2,parameter3 from v$event_name where name like 'latch: cache buffers chains';NAME                           PARAMETER1           PARAMETER2           PARAMETER3------------------------------ -------------------- -------------------- --------------------latch: cache buffers chains    address              number               tries

3.2.cache buffers lru chains latch & working set

LRU和LRUW总是成对(pair)出现,这一对链被称作working set,即working set=LRU+LRUW。Oracle使用多个working set,而一个cache buffers lru chains latch管理一个working set。要检索LRU或LRUW的进程必须获得cache buffers lru chains latch。可以通过隐含参数_db_block_lru_latches查看cache buffers lru chains latch的数量:

SQL> select ksppinm, ksppstvl from x$ksppi x, x$ksppcv y  2  where (x.indx = y.indx) and (translate(ksppinm, '_', '#')) like '_db_block_lru_latches';KSPPINM                        KSPPSTVL------------------------------ --------------------_db_block_lru_latches          16
还可以通过如下方式查看cache buffers lru chains latch数量:
SQL> select count(*) from v$latch_children where name like 'cache buffers lru chain';  COUNT(*)----------        32
可以看见这里实际创建的cache buffers lru chains latch数量是隐含参数_db_block_lru_latches值的两倍。这是因为最多16个这一latch数是由CPU数决定的。
为获得cache buffers lru chains latch发生争用,这个过程中的等待事件是:
SQL> select name,parameter1,parameter2,parameter3 from v$event_name where name like 'latch: cache buffers lru chain';NAME                           PARAMETER1           PARAMETER2           PARAMETER3------------------------------ -------------------- -------------------- --------------------latch: cache buffers lru chain address              number               tries

3.3.buffer cache 优化

从8.0以后,oracle提供了三种类型的buffer cache,分别是default、keep、recyle。keep和recycle是可选的,default必须存在。通常将经常访问的对象放入keep类型的buffer cache里,而将不常访问的大表放入recycle类型的buffer cache里。其他没有指定buffer cache类型的对象都将进入default类型的buffer cache里。如果没有指定buffer_pool短语,则表示该对象进入default类型的buffer cache。

这里要说明的是,从名字上看,很容易让人误以为这三种buffer cache提供了三种不同的管理内存数据块的机制。但事实上,它们之间在管理和内部机制上没有任何的区别。它们仅仅是为DBA们提供了一个选择,就是能够将数据库对象分成“非常热的”、“比较热的”和“不热的”这三种类型。因为数据库中总会存在一些“非常热”的对象,它们频繁的被访问。而如果某个时候系统偶尔做了一次大表的全表扫描,就有可能将这些对象清除出内存。为了防止这种情况的发生,我们可以设置keep类型的buffer cache,并将这种对象都移入keep buffer cache中。同样的,数据库中也总会有一些很大的表,可能每天为了生成一张报表,而只需要访问一次就可以了。但有可能就是这么一次访问,就将大部分的内 存数据块清除出了buffer cache。为了避免这种情况的发生,可以设置recycle类型的buffer cache,并将这种偶尔访问的大表移入recycle buffer cache。

毫无疑问,如果你要设置这三种类型的 buffer cache,你需要自己研究并等于你的数据库中的对象进行分类,并计算这些对象的大小,从而才能够正确的把它们放入不同的buffer cache。但是,不管怎么说,设置这三种类型的buffer cache只能算是最低层次的优化,也就是说在你没有任何办法的情况下,可以考虑设置他们。但是如果你能够优化某条buffer gets非常高SQL使其buffer gets降低50%的话,就已经比设置多个buffer cache要好很多了。

9i以后还提供了可以设置多种数据块尺寸(2、4、8、16 或 32k)的buffer cache,以便存放不同数据块尺寸的表空间中的对象。OLTP环境下,倾向于使用较小的数据块,而OLAP环境下,由于基本 都是执行全表扫描,因此倾向于使用较大的数据块。

原创粉丝点击