深入浅出Oracle学习笔记(5)

5Buffer CacheShared Pool原理

 

LRUDirty List

Buffer Cache中,Oracle通过几个链表进行内存管理。

LRU list用于维护内存中的Buffer,按照LRU算法进行管理。数据库初始化时,所有的Buffer都被HashLRU list上管理。当需要从数据文件上读取数据时,首先要在LRU List上寻找FreeBuffer,然后读取数据到Buffer Cache中;当数据被修改之后,状态变为Dirty,就可以被移动至Dirty ListDirty List上的都是候选的可以被DBWR写出到数据文件的Buffer,一个Buffer要么在LRU List上,要么在Dirty List上存在,不能同时存在于多个list

Buffer Cache的原理及使用:

当一个Server进程需要读数据到Buffer Cache中时,首先必须判断该数据在Buffer中是否存在,如果存在且可用,则获取该数据,根据LRU算法在LRU List上移动该Block;如果Buffer中不存在数据,则需要从数据文件上读取。

在读取数据之前,Server进程需要扫描LRU List寻找FreeBuffer,扫描过程中Server进程会把发现的所有已经被修改过的Buffer移动到Checkpoint Queue上,这些Dirty Buffer随后可以被写出到数据文件。

如果Checkpoint Queue超过了阈值,Server进程就会通知DBWn去写出脏数据;这也是触发DBWn写的一个条件,这个阈值曾经提到是25%,也就是当检查点队列超过25%满就会触发DBWn的写操作:

SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbldq';

KVITTAG            KVITVAL KVITDSC

--------------- ---------- --------------------------------------------------

kcbldq                  25 large dirty queue if kcbclw reaches this

如果Server进程扫描LRU超过一个阈值仍然不能找到足够的Free Buffer,将停止寻找,转而通知DBWn去写出脏数据,释放内存空间。这个数字是40%,也就是说当Server进程扫描LRU超过40% 还没能找到足够的Free Buffer就会停止搜索,通知DBWn执行写出,这时进程会处于free buffer wait等待:

SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbfsp';

KVITTAG       KVITVAL KVITDSC

---------- ---------- -------------------------------------------------------

kcbfsp             40 Max percentage of LRU list foreground can scan for free

同时由于增量检查点的引入,DBWn也会主动扫描LRU List,将发现的Dirty Buffer移至Checkpoint Queue,这个扫描也受到一个内部约束,在Oracle 9iR2中这个比例是25%

SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbdsp';

KVITTAG       KVITVAL KVITDSC

---------- ---------- -------------------------------------------------------

kcbdsp             25 Max percentage of LRU list dbwriter can scan for dirty

 

找到足够的Buffer之后,Server进程就可以将Buffer从数据文件读入Buffer Cache

如果读取的Block不满足读一致性要求,则Server进程需要通过当前Block版本和回滚段构造前镜像返回给用户。

Oracle 8i开始,LRU ListDirty List又分别增加了辅助ListAuxiliary list),用于提高管理效率。引入了辅助List之后,当数据库初始化时,Buffer首先存放在LRU的辅助List上(Auxiliary rpl_lst),当被使用后移动到LRU的主List上(Main rpl_lst),这样当用户进程搜索Free Buffer时,就可以从LRU-AUX List开始,而DBWR搜索Dirty Buffer时,则可以从LRU-MAIN List开始,从而提高了搜索效率和数据库性能。

可以通过如下命令转储Buffer Cache的内容,从而清晰地看到以上描述得数据结构:

Alter session set events ‘immediate trace name buffers level 4’;

不同Level转储内容的详细程度不同,此命令的可用级别主要有1~10级,其中各级别的含义如下:

Level 1:仅包含Buffer Headers信息;

Level 2:包含Buffer HeadersBuffer概要信息转储;

Level 3:包含Buffer Headers和完整Buffer内容转储;

Level 4Level 1+Latch转储+LRU队列;

Level 5Level 4+Buffer概要信息转储;

Level 6Level 7Level 4+完整的Buffer内容转储;

Level 8Level 4 +显示users/waiters信息;

Level 9Level 5 +显示users/waiters信息;

Level 10Level 6 +显示users/waiters信息。

 

Cache Buffers Lru Chain闩锁竞争与解决

当进程需要读数据到Buffer Cache时,或Cache Buffer根据LRU算法进行管理时,就不可避免地要扫描LRU List获取可用Buffer 或更改Buffer状态。OracleBuffer Cache是共享内存,可以为众多并发进程并发访问,所以在搜索过程中必须获取LatchLatchOracle的一种串行锁机制,用于保护共享内存结构),锁定内存结构,防止并发访问损坏内存中的数据。

这个用于锁定LRULatch就是经常见到的Cache Buffers Lru Chain

SQL> select addr,latch#,name,gets,misses,immediate_gets,immediate_misses

  2  from v$latch where name='cache buffers lru chain';

ADDR         LATCH# NAME                            GETS     MISSES

-------- ---------- ------------------------- ---------- ----------

IMMEDIATE_GETS IMMEDIATE_MISSES

-------------- ----------------

01FED330         92 cache buffers lru chain        12409          3

          5849                0

 

Cache Buffers Lru Chain Latch存在多个子Latch,其数量受隐含参数_db_block_lru_latches控制。可以从v$latch_children视图查看当前各子Latch使用情况:

SQL> select addr,child#,name,gets,misses,immediate_gets,immediate_misses

  2  from v$latch_children where name='cache buffers lru chain';

 

ADDR         CHILD# NAME                            GETS     MISSES

-------- ---------- ------------------------- ---------- ----------

IMMEDIATE_GETS IMMEDIATE_MISSES

-------------- ----------------

67EA55E8          8 cache buffers lru chain           22          0

             0                0

 

67EA511C          7 cache buffers lru chain           22          0

             0                0

 

67EA4C50          6 cache buffers lru chain           22          0

             0                0

 

67EA4784          5 cache buffers lru chain           22          0

             0                0

 

67EA42B8          4 cache buffers lru chain           22          0

             0                0

 

67EA3DEC          3 cache buffers lru chain        12553          3

          6137                0

 

67EA3920          2 cache buffers lru chain           22          0

             0                0

 

67EA3454          1 cache buffers lru chain           22          0

             0                0

 

如果该Latch竞争激烈,通常有如下方法可以采用:

适当增大Buffer Cache,这样可以减少读数据到Buffer Cache的机会,减少扫描LRU List的竞争;

可以适当增加LRU Latch数量,修改_db_block_lru_latches参数,该方法不推荐使用;

通过多缓冲池技术,可以减少不希望的数据老化和全表扫描等操作对Default池的冲击,从而可以减少竞争。

 

Cache Buffer Chain闩锁竞争与解决

LRUDirty List 这两个内存结构之外,Buffer Cache的管理还存在另外两个重要的数据结构:Hash BucketCache Buffer Chain

可以想象如果所有的Buffer Cache中的所有Buffer都通过同一个结构管理,当需要确定某个BlockBuffer中是否存在时,将需要遍历整个结构,性能会相当低下。

为了提高效率,Oracle引入了Bucket的数据结构,Oracle把管理的所有Buffer通过一个内部的Hash算法运算后,存放到不同的Hash Bucket中,这样通过Hash Bucket分割之后,众多的Buffer被分布到一定数量的Bucket之中,当用户需要在Buffer中定位数据是否存在时,只需要通过同样的算法获得Hash值,然后到相应得Bucket中查找少量的Buffer即可确定。

Bucket内部通过Cache Buffer ChainCache Buffer Chain是一个双向链表)将所有的Buffer通过Buffer Header信息联系起来。Buffer Header存放的是对应数据块的概要信息,包括数据块的文件号、块地址、状态等。要判断数据块在Buffer中是否存在,检查Buffer Header即可确定。

对应每个Bucket,只存在一个Chain,当用户试图搜索Cache Buffer Chain时,必须首先获得Cache Buffer Chain Latch

总结:

Oracle 8i开始,Bucket的数量比以前大大增加;通过增加的Bucket的“稀释”使得每个Bucket上的Buffer数量大大减少。

Oracle 8i之前,_db_block_hash_latches的数量和Bucket的数量是一致的,每个Latch管理一个Bucket;从Oracle 8i开始每个Latch需要管理多个Bucket,由于每个Bucket上的Buffer数量大大降低,所以Latch的性能反而得到提高。

每个Bucket存在一条Cache Buffer Chain

Buffer Header上存在指向具体Buffer的指针。

 

X$BHBuffer Header

Buffer Header数据可以从数据库的字典表中查询得到,这张字典表是X$BH。每个BufferX$BH中都存在一条记录。

 

X$BH中有一个重要字段TCHTCHTouch的缩写,表示一个Buffer的访问次数,Buffer 被访问的次数越多,说明该Buffer越“抢手”,也就可能存在热点块竞争的问题。

SQL> select *

  2  from (select addr,ts#,file#,dbarfil,dbablk,tch

  3  from x$bh order by tch desc)

  4  where rownum<11;

 

ADDR            TS#      FILE#    DBARFIL     DBABLK        TCH

-------- ---------- ---------- ---------- ---------- ----------

0430A6E8          0          1          1       1498        652

0430C9FC          0          1          1       7882        142

0430A5A4          0          1          1       7938        142

0430C9FC          0          1          1       8202        142

0430C9FC          0          1          1       7873        107

0430C9FC          0          1          1       7889        107

0430C9FC          0          1          1       8233        107

0430C9FC          5          5          5      18444        106

0430C9FC          5          5          5      18516        106

0430C9FC          0          1          1       9017        106

 

再结合dba_extents中的信息,可以查询到这些热点Buffer都来自哪些对象:

SQL> select e.owner,e.segment_name,e.segment_type

  2  from dba_extents e,

  3  (select *

  4  from (select addr,ts#,file#,dbarfil,dbablk,tch

  5  from x$bh order by tch desc)

  6  where rownum<11) b

  7  where e.relative_fno=b.dbarfil

  8  and e.block_id<=b.dbablk

  9  and e.block_id+e.blocks>b.dbablk;

OWNER           SEGMENT_NAME              SEGMENT_TYPE

--------------- ------------------------- -------------------------

SYS             I_JOB_NEXT                INDEX

SYS             I_FILE#_BLOCK#            INDEX

SYS             C_FILE#_BLOCK#            CLUSTER

SYS             I_FILE#_BLOCK#            INDEX

SYS             C_USER#                   CLUSTER

SYS             I_OBJ1                    INDEX

SYS             OBJ$                      TABLE

SYS             OBJ$                      TABLE

SYS             C_FILE#_BLOCK#            CLUSTER

SYS             C_TS#                     CLUSTER

 

转储Buffer内容获取跟踪文件:

Alter session set events ‘immediate trace name buffers level 10’;

 

Shared Pool的基本原理

Oracle通过Shared Pool来实现SQL共享、减少代码硬解析等,从而提高数据库的性能。

 

通过如下命令转储Shared Pool共享内存的内容:

Alter session set events ‘immediate trace name heapdump level 2’;

 

Shared Pool通过Free Lists管理Free内存块(Chunk),Free的内存块(Chunk)按不同size被划分到不同的部分(Bucket)进行管理。

初始时,数据库启动以后,Shared Pool多数是连续内存块,但是当空间分配使用以后,内存块开始被分割,碎片开始出现,Bucket列表开始变长。Oracle请求Shared Pool空间时,首先进入相应的Bucket进行查找。如果找不到,则转向下一个非空的Bucket,获取第一个Chunk。如果找不到,则转向下一个非空的Bucket,获取第一个Chunk。分割这个Chunk,剩余部分会进入相应的Bucket,进一步增加碎片。

最终的结果是,由于不停分割,每个Bucket上的内存块会越来越多,越来越碎小。碎片过多会导致搜索Free List的时间过长,而Free Lists的管理和搜索都需要获得和持有一个非常重要的Latch,就是Shared Pool LatchLatchOracle数据库内部提供的一种低级锁,通过串行机制保护共享内存不被并发更新/修改所损坏。Latch的持有通常都非常短暂 ,但是对于一个繁忙的数据库,这个串行机制往往会成为极大的性能瓶颈。

如果Free Lists链表过长,搜索这个Free Lists的时间就会变长,从而导致Shared Pool Latch被长时间持有,在一个繁忙的系统中,这会引起严重的Shared Pool Latch竞争。

Oracle 9i中,为了增加对于大共享池的支持,Shared Pool Latch从原来的一个增加到现在的7个。

SQL> select addr,name,gets,misses,spin_gets

  2  from v$latch_children where name='shared pool';

ADDR     NAME                  GETS     MISSES  SPIN_GETS

-------- --------------- ---------- ---------- ----------

031D3724 shared pool              0          0          0

031D365C shared pool              0          0          0

031D3594 shared pool              0          0          0

031D34CC shared pool              0          0          0

031D3404 shared pool              0          0          0

031D333C shared pool              0          0          0

031D3274 shared pool          90710          1          0

 

 

 

判断和解决ORA-04031错误

当尝试在共享池分配大块的连续内存失败(很多时候是由于碎片过多,而并非真是内存不足)时,Oracle首先清除共享池中当前没使用的所有对象,使空闲内存块合并。如果仍然没有足够大的单块内存可以满足 需要,就会产生ORA-04031错误。

 

绑定变量和cursor_sharing

如果shared_pool_size设置得足够大,又可以排除Bug的因素,那么大多数的ORA-04031错误都是由共享池中的大量SQL代码等导致了过多的内存碎片而引起的,可能的主要原因有:

SQL没有足够的共享;

大量不必要的解析调用;

没有使用绑定变量。

 

使用Flush Shared Pool缓解共享池问题

Alter system flush shared_pool;

该命令通过刷新共享池可以帮助合并碎片(Small Chunks),强制老化SQL,释放共享池,但这通常是不推荐的做法,因为:

Flush Shared Pool会导致当前未使用的cursor被清除出共享池,如果这些SQL随后需要执行,那么数据库将经历大量的硬解析,系统将会经历严重的CPU争用,数据库将会产生严重的latch竞争。

如果应用没有使用绑定变量,大量类似的SQL不停执行,那么Flush Shared Pool可能只能带来短暂的改善,数据库很快就会回到原来的状态。

如果Shared Pool很大,并且系统非常繁忙,刷新Shared Pool可能会导致系统挂起,对于类似系统尽量在系统空闲时进行。

Oracle 9i开始,Oracle的共享池算法发生了改变,Flush Shared Pool的方法已经不再推荐使用。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15203236/viewspace-600647/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/15203236/viewspace-600647/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值