oracle cache buffer lru chain(一)
今天是2013-09-09,这个月 我要 做一个大事情,就是把oracle内存结构研究好,这是第一篇笔记。
oracle database buffer cache:
一、
1)buffer cache 定义:
数据缓存区是copy的数据块信息存在buffercache中sga的一个块区域。所有的客户端进程都能够并发的访问实例来共享这buffer cache。
2)buffer cache实现的目标:
1、优化磁盘的物理读写
2、在buffer cache中保持频繁访问,延迟写入磁盘。
3)buffer状态
数据库使用内部算法来管理buffer cache,没个buffer存在如下情况之一。
1、unused
这种buffer是可以使用的,因为它从来没有被使用或是当前已经使用过了的。这种状态的buffer是最容易被使用的。
2、clean
这种缓冲区是最近使用的并且包含数据块的读一致性。这种数据块包含数据但是是干净的,所以没有必要进行检查点。数据库可以对它进行pin然后重新使用它
3、dirty
这种数据缓冲区包含被编辑的数据块,并且这些数据块还没有被写入磁盘。数据库必须对它进行checkpoint之后,然后再重新使用它。
Every buffer has an access mode: pinned orfree (unpinned). A buffer is "pinned" in the cache so that it doesnot age out of memory while a user session accesses it. Multiple sessionscannot modify a pinned buffer at the same time.
The database uses a sophisticated algorithmto make buffer access efficient. Pointers to dirty and nondirty buffers existon the same least recently used (LRU) list, which has a hot end and cold end. Acold buffer is one that has not been recently used. A hot buffer is frequentlyaccessed and has been recently used.
4)buffer write情况
数据库使用dbwn进程来完成数据从buffer cache写入到datafile中。该进程个数可以通过db_writer_processes参数进行设置。
另外在dbwn进程在执行写的条件有如下:
1)当服务今次那个不能够发现clean请求的buffer的时候,将会执行写操作,这个也受一个隐含参数控制:
SQL> select kvitval,kvittag,kvitdsc fromx$kvit where kvittag='kcbfsp';
KVITVAL KVITTAG KVITDSC
-------------------------------------- ----------------------------
40 kcbfsp Max percentage of LRU list foreground can scan for free
SQL>
还有就是dirty lru达到一定值的时候,需要进行写出操作,这个值也收一个隐含参数控制:
SQL> select kvitval,kvittag,kvitdsc fromx$kvit where kvittag='kcbldq';
KVITVAL KVITTAG KVITDSC
------------------------------------------------------------------
25 kcbldq large dirty queue if kcbclw reaches this
SQL>
2、在完成检查点之前需要写出buffercache。
5)buffer 读的情况
对于oracle clean或是unused buffer的数量很少的时候,那么数据库就会执行删除的操作。这种机制取决于flash cache是否启动了
注意:数据库的smart flash cache只能在solaris和oracle enterprise linux中使用。
flash cache 没有启动:
数据库就会重新使用clean的buffer,或是覆盖之后进行使用。如果覆盖之后的数据过会又被使用,那么只能进行物理的读写操作,把数据文件的信息重新读取到buffer中。
flash cahce 启动:
当dbwn进程能够把clean buffer的body写入到flash cache中,然后再内存中重新使用buffer,这时候数据库会保持原先的数据header存在于内存中的lru链中,数据存在flash cache中。当这个buffer 在此被使用的时候,就会从flash cache进行读取而不是进行物理的读写操作,从而提高了效率。
对于lru链的学习,参考:http://yeshaochen.blog.51cto.com/3155801/913342(灰常经典)
当一个客户端请求数据的时候,来看一下服务进程执行过程:
1) 首先服务进程搜索全部buffercache,如果找到了,那么就进行逻辑读取信息返回给client
2) 服务进程搜索在flashcache lru list中的buffer header信息。
如果找到了buffer header,那么就会执行最优化的把信息从flash cache中读取到memory cache。
3) 如果服务进程没有找到需要的buffer信息,那么就会进行如下两部:
1、 执行物理读取数据文件信息,把数据块copy到内存中
2、 在buffer 中执行逻辑读取然后返回给用户信息
另外在来看一下flash cache这一部分知识,请查看联机手册:
http://docs.oracle.com/cd/E11882_01/server.112/e25494/memory.htm#BABHEDBH
二、
在oracle的内存中存在很多list进行管理,转储一下buffer然后查看一下具体情况:
对于内存转储有如下等级;
Level 1 :包含buffer headers信息
Level 2:包含buffer headers和buffer概要信息
Level 3:包含buffer headers和完整的buffer内容转储
Level 4:level 1+ latch转储+LRU队列
Level 5: level4 + buffer概要信息转储
Level 6和level 7:level4+完整的buffer内存转储
Level 8:level4+显示users/waiters信息
Level 9:level5 +显示users/waiters信息
Level 10:level 6+显示users/waiters信息:
Eg:
SQL> alter system set events 'immediatetrace name buffer level 4';
System altered.
注意:转储的等级越高,trace文件就越大,内容就越详细,但是注意:max_dump_file_size 参数的设置为没有限制大小,不要在现网中进行很高等级的buffer cache转储操作。
查看信息:
Dump of buffer cache at level 4 fortsn=2147483647, rdba=0
(WS) size: 0 (0) wsid: 1 state: 0 pool: 1
(WS_REPL_LIST) main_prev: 0x76e40858 main_next: 0x76e40858 aux_prev:0x76e40868 aux_next: 0x76e40868
curnum: 0 auxnum: 0
cold: 76e40858 hbmax: 0 hbufs: 0
(WS_WRITE_LIST) main_prev: 0x76e40888 main_next: 0x76e40888 aux_prev:0x76e40898 aux_next: 0x76e40898
curnum: 0 auxnum: 0
(WS_XOBJ_LIST) main_prev: 0x76e408b8 main_next: 0x76e408b8 aux_prev:0x76e408c8 aux_next: 0x76e408c8
curnum: 0 auxnum: 0
(WS_XRNG_LIST) main_prev: 0x76e408e8 main_next: 0x76e408e8 aux_prev:0x76e408f8 aux_next: 0x76e408f8
curnum: 0 auxnum: 0
(WS_REQ_LIST) main_prev: 0x76e40918 main_next: 0x76e40918 aux_prev:0x76e40928 aux_next: 0x76e40928
curnum: 0 auxnum: 0
(WS_L2WRT_LIST) main_prev: 0x76e40948 main_next: 0x76e40948 aux_prev:0x76e40958 aux_next: 0x76e40958
curnum: 0 auxnum: 0
(WS_L2REPL_LIST) main_prev: 0x76e40978 main_next: 0x76e40978 aux_prev:0x76e40988 aux_next: 0x76e40988
curnum: 0 auxnum: 0
(WS_L2KEEP_LIST) main_prev: 0x76e409a8 main_next: 0x76e409a8 aux_prev:0x76e409b8 aux_next: 0x76e409b8
curnum: 0auxnum: 0
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
MAIN RPL_LSTQueue header (NEXT_DIRECTION)[NULL]
MAIN RPL_LSTQueue header (PREV_DIRECTION)[NULL]
AUXILIARYRPL_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARYRPL_LST Queue header (PREV_DIRECTION)[NULL]
MAIN WRT_LSTQueue header (NEXT_DIRECTION)[NULL]
MAIN WRT_LSTQueue header (PREV_DIRECTION)[NULL]
AUXILIARYWRT_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARYWRT_LST Queue header (PREV_DIRECTION)[NULL]
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
MAIN RPL_LSTQueue header (NEXT_DIRECTION)[0x687fb9d0,0x6c3f5610]
0x687fb8e8=>0x687fba18=>0x687fbb48=>0x687fbc78=>0x687fbda8=>0x687fbed8=>0x687fc008=>0x687fc138
0x687fc268=>0x687fc5f8=>0x687fc398=>0x687fc4c8=>0x68bed748=>0x68bed878=>0x68bed9a8=>0x68bedad8
0x68bedc08=>0x68bedd38=>0x68bede68=>0x68bedf98=>0x68bee0c8=>0x68bee1f8=>0x68bee328=>0x68bee458
0x68bee588=>0x68bee6b8=>0x68bee7e8=>0x68bee918=>0x68beea48=>0x68fec318=>0x68fec448=>0x68fec578
0x68fec6a8=>0x68fec7d8=>0x68fec908=>0x68feca38=>0x68fecb68=>0x68fecc98=>0x68fecdc8=>0x68fecef8
0x68fed028=>0x68fed158=>0x68fed288=>0x68fed3b8=>0x68fed4e8=>0x68fed618=>0x693eaee8=>0x693eb018
0x693eb148=>0x693eb278=>0x693eb3a8=>0x693eb4d8=>0x693eb608=>0x693eb738=>0x693eb868=>0x693eb998
0x693ebac8=>0x693ebbf8=>0x693ebd28=>0x693ebe58=>0x693ebf88=>0x693ec0b8=>0x693ec1e8=>0x697e9ab8
0x697e9be8=>0x697e9d18=>0x697e9e48=>0x697e9f78=>0x697ea0a8=>0x697ea1d8=>0x697ea308=>0x697ea438
0x697ea568=>0x697ea698=>0x697ea7c8=>0x697ea8f8=>0x697eaa28=>0x697eab58=>0x697eac88=>0x697eadb8
0x69be8688=>0x69be87b8=>0x69be88e8=>0x69be8a18=>0x69be8b48=>0x69be8c78=>0x69be8da8=>0x69be8ed8
0x69be9008=>0x69be9138=>0x69be9268=>0x69be9398=>0x69be94c8=>0x69be95f8=>0x69be9728=>0x69be9858
0x69be9988=>0x69fe7258=>0x69fe7388=>0x69fe74b8=>0x69fe75e8=>0x69fe7718=>0x69fe7848=>0x69fe7978
0x69fe7aa8=>0x69fe7bd8=>0x69fe7d08=>0x69fe7e38=>0x69fe7f68=>0x69fe8098=>0x69fe81c8=>0x69fe82f8
0x69fe8428=>0x69fe8558=>0x6a3e5e28=>0x6a3e5f58=>0x6a3e6088=>0x6a3e61b8=>0x6a3e62e8=>0x6a3e6418
0x6a3e6548=>0x6a3e6678=>0x6a3e67a8=>0x6a3e68d8=>0x6a3e6a08=>0x6a3e6b38=>0x6a3e6c68=>0x6a3e6d98
0x6a3e6ec8=>0x6a3e6ff8=>0x6a3e7128=>0x6a7e49f8=>0x6a7e4b28=>0x6a7e4c58=>0x6a7e4d88=>0x6a7e4eb8
0x6a7e4fe8=>0x6a7e5118=>0x6a7e5248=>0x6a7e5378=>0x6a7e54a8=>0x6a7e55d8=>0x6a7e5708=>0x6a7e5968
0x6a7e5838=>0x6a7e5a98=>0x6a7e5bc8=>0x6a7e5cf8=>0x6abe35c8=>0x6abe36f8=>0x6abe3828=>0x6abe3958
0x6abe3a88=>0x6abe3bb8=>0x6abe3ce8=>0x6abe3e18=>0x6abe3f48=>0x6abe4078=>0x6abe41a8=>0x6abe42d8
0x6abe4408=>0x6abe4538=>0x6abe4668=>0x6abe4798=>0x6abe48c8=>0x6afe2198=>0x6afe22c8=>0x6afe23f8
0x6afe2528=>0x6afe2658=>0x6afe2788=>0x6afe28b8=>0x6afe29e8=>0x6afe2b18=>0x6afe2c48=>0x6afe2d78
0x6afe2ea8=>0x6afe2fd8=>0x6afe3108=>0x6afe3238=>0x6afe3368=>0x6afe3498=>0x6b3fa128=>0x6b3fa258
0x6b3fa388=>0x6b3fa4b8=>0x6b3fa5e8=>0x6b3fa718=>0x6b3fa848=>0x6b3fa978=>0x6b3faaa8=>0x6b3fabd8
0x6b3fad08=>0x6b3fae38=>0x6b3faf68=>0x6b3fb098=>0x6b3fb1c8=>0x6b3fb2f8=>0x6b3fb428=>0x6b7f8cf8
0x6b7f8e28=>0x6b7f8f58=>0x6dbe10f8=>0x6b7f9088=>0x6b7f91b8=>0x6b7f92e8=>0x6b7f9418=>0x6b7f9548
0x6b7f9678=>0x6b7f97a8=>0x6b7f98d8=>0x6b7f9a08=>0x6b7f9b38=>0x6b7f9c68=>0x6b7f9d98=>0x6b7f9ec8
0x6b7f9ff8=>0x6bbf78c8=>0x6bbf79f8=>0x6dbe0d68=>0x6bbf7b28=>0x6bbf7c58=>0x6bbf7d88=>0x6bbf7eb8
0x6bbf7fe8=>0x6d3f0468=>0x6bbf8118=>0x6bbf8248=>0x6bbf8378=>0x6bbf84a8=>0x6bbf85d8=>0x6bbf8708
0x6bbf8838=>0x6bbf8968=>0x6bbf8a98=>0x6bbf8bc8=>0x6bff6498=>0x6bff65c8=>0x6bff66f8=>0x6bff6828
0x6bff6958=>0x6bff6a88=>0x6bff6bb8=>0x6bff6ce8=>0x6bff6e18=>0x6bff6f48=>0x6bff7078=>0x6bff71a8
0x6bff72d8=>0x6bff7408=>0x6bff7538=>0x6bff7668=>0x6cbf2f28=>0x6bff7798=>0x6c3f5068=>0x6c3f5198
0x6c3f52c8=>0x6c3f53f8=>0x6c3f5788=>0x6c3f5c48=>0x6c3f6108=>0x6c7f3d68=>0x6c7f3e98=>0x6c7f3fc8
0x6c7f4ba8=>0x6cbf2808=>0x6cbf32b8=>0x6d7ef888=>0x6cbf3648=>0x6cff1898=>0x6cff1af8=>0x6cff1c28
0x6cff1d58=>0x6cff1e88=>0x6cff1fb8=>0x6cff20e8=>0x6cff2218=>0x6cff2348=>0x6cff2478=>0x6cff25a8
0x6cff26d8=>0x6d3effa8=>0x6d3f00d8=>0x6d3f0208=>0x6d3f0338=>0x6d3f0598=>0x6d3f06c8=>0x6d3f07f8
0x6d3f0928=>0x6d3f0a58=>0x6d3f0b88=>0x6d3f0cb8=>0x6d3f0de8=>0x6d3f0f18=>0x6d3f1048=>0x6d3f1178
0x6d3f12a8=>0x6d7eeb78=>0x6d7eeca8=>0x6d7eedd8=>0x6d7eef08=>0x6d7ef038=>0x6d7ef168=>0x6d7ef298
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
CHAIN: 0 LOC:0x76d3bd50 HEAD: [NULL]
CHAIN: 1 LOC:0x76d3bd60 HEAD: [NULL]
CHAIN: 2 LOC:0x76d3bd70 HEAD: [0x6c7fa698,0x6c7fa698]
BH (0x6c7fa5e8) file#: 1 rdba: 0x0040fc5c(1/64604) class: 1 ba: 0x6c7a0000
set: 75 pool 3 bsz: 8192 bsi: 0 sflg: 2pwc: 14,28
dbwrid: 14 obj: 2 objn: 4 tsn: 0 afn: 1hint: f
hash: [0x76d3bd70,0x76d3bd70] lru:[0x6c7fa5a0,0x6c7fa800]
lru-flags: debug_dump moved_to_tail
ckptq: [NULL] fileq: [NULL] objq:[0x6c7fa828,0x6c7fa5c8]
st: XCURRENT md: NULL tch: 1
flags: only_sequential_access
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN:[0xffff.ffffffff] HSUB: [65535]
cr pin refcnt: 0 sh pin refcnt: 0
。。。。。。。。。。。。。。。。。。。。。。。
Ws:就是工作集,oracle把所有链表按照功能不同分为很多ws,每个ws都有一个wsid这就是工作集编号。另外还可以看到有AUXILIARY,这是在数据库初始化的时候分配buffer到该链表,然后数据库搜free memory 就在这个链表开始,把分配空间的链表内容在放到main 链表上,从而搜索空位置在auxiliary list开始,dbwr搜索dirty list从main链开始,提高了效率。我们熟知的checkpoint queue存在于shared pool中
SQL> select* from v$sgastat where name like '%Checkpoint%';
POOL NAME BYTES
-------------------------------------- ----------
sharedpool Checkpoint queue 6159360
SQL>
另外没个链表都被latch保护,常见的就是cachebuffer lru chain。
在此先看一下v$latch视图。
Latch请求分为两类:
1. 一类为:willing-to-wait,也就是说,请求latch如果请求不到,那么先歇会,到达一定时间在去请求,直到请求到该latch。在misses字段记录了请求失败的次数,失败一次该值加1.
2. 另一类为:immedate,也就是说当进行latch请求的时候,请求道最好,如果请求不到那么程序会继续下去,immedate_misses记录了该请求失败的次数。
另外还有spin_gets,也就是说,当在willing-to-wait模式下,第一次请求latch失败,但是在之后请求都成功的次数。
Wait_time;请求latch等待的时间。单位为:微妙(inmicroseconds)
Sleeps:Number oftimes a willing-to-wait latch request resulted in a session sleeping whilewaiting for the latch,成功获取latch前sleep多少次。
好了,我们可以通过v$latch视图确认,是否buffercache存在latch竞争问题:
Eg:
SQL> setlinesize 200
SQL> r
1* selectname,gets,misses,sleeps,immediate_gets,immediate_misses,wait_time from v$latchwhere name like '%cache buffer%'
NAME GETS MISSES SLEEPS IMMEDIATE_GETS IMMEDIATE_MISSES WAIT_TIME
-------------------------------------- ---------- ---------- -------------- ---------------- ----------
cache bufferslru chain 9863 0 0 21850 0 0
cache bufferschains 426674 0 0 138947 0 0
cache bufferhandles 984 0 0 0 0 0
SQL>
可以看到有三个latch,一个为cachebuffer lru chain,一个为cache buffers chains另一为cache bufferhandles;
先看第一个:
当用户进程需要读取数据到buffercache的时候,就需要扫描lru list,但是这个buffer cache是共享的,因此必须有一个latch进行保护,因此就需要锁定内存结构,防止并发修改破坏数据,对于数据库的访问,buffer的存取就需要多次方位lru list,从而会产生cache buffer lru chain的竞争。
另外对于该latch 还有很多子latch,可以通过v$latch_children查看:
Eg:
SQL> selectname,gets,misses,sleeps,immediate_gets,immediate_misses,spin_gets,wait_timefrom v$latch_children where name like '%cache buffers lru chain%';
NAME GETS MISSES SLEEPS IMMEDIATE_GETS IMMEDIATE_MISSES SPIN_GETS WAIT_TIME
-------------------------------------- ---------- ---------- -------------- ---------------- --------------------
cache bufferslru chain 0 0 0 0 0 0 0
cache bufferslru chain 12 0 0 3 0 0 0
cache bufferslru chain 0 0 0 0 0 0 0
cache bufferslru chain 12 0 0 3 0 0 0
cache bufferslru chain 0 0 0 0 0 0 0
cache bufferslru chain 12 0 0 3 0 0 0
cache bufferslru chain 0 0 0 0 0 0 0
cache bufferslru chain 12 0 0 3 0 0 0
cache bufferslru chain 0 0 0 0 0 0 0
cache bufferslru chain 12 0 0 3 0 0 0
cache bufferslru chain 0 0 0 0 0 0 0
对于子latch的数量受一个隐含参数控制:
Eg:
SQL>@getpar.sql
Enter value forpar: block_lru_latches
KSPPINM KSPPSTVL KSPPDESC
---------------------------------- ------------------------
_db_block_lru_latches 240 number of lru latches
SQL>
对于,这种竞争我们采用如下办法解决:
1) 修改buffercache大小,减小物理读的次数
2) 调整_db_block_lru_latches的个数,增加latch个数
3) 优化sql语句减少读取lrulist的次数
4)设置oracle多缓冲池技术,一个是recycle池另一个是keep池,以此来减少数据老化和全表扫描对default buffer 的压力。