oracle 权限问题9017,深入浅出Oracle学习笔记(5)

第5章Buffer Cache与Shared Pool原理

LRU与Dirty List

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

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

Buffer Cache的原理及使用:

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

在读取数据之前,Server进程需要扫描LRU List寻找Free的Buffer,扫描过程中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';

KVITTAGKVITVAL KVITDSC

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

kcbldq25 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';

KVITTAGKVITVAL KVITDSC

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

kcbfsp40 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';

KVITTAGKVITVAL KVITDSC

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

kcbdsp25 Max percentage of LRU list dbwriter can scan for dirty

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

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

从Oracle 8i开始,LRU List和Dirty List又分别增加了辅助List(Auxiliary 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 Headers和Buffer概要信息转储;

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

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

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

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

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

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

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

Cache Buffers Lru Chain闩锁竞争与解决

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

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

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

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

ADDRLATCH# NAMEGETSMISSES

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

IMMEDIATE_GETS IMMEDIATE_MISSES

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

01FED33092 cache buffers lru chain124093

58490

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

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

ADDRCHILD# NAMEGETSMISSES

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

IMMEDIATE_GETS IMMEDIATE_MISSES

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

67EA55E88 cache buffers lru chain220

00

67EA511C7 cache buffers lru chain220

00

67EA4C506 cache buffers lru chain220

00

67EA47845 cache buffers lru chain220

00

67EA42B84 cache buffers lru chain220

00

67EA3DEC3 cache buffers lru chain125533

61370

67EA39202 cache buffers lru chain220

00

67EA34541 cache buffers lru chain220

00

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

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

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

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

Cache Buffer Chain闩锁竞争与解决

在LRU和Dirty List这两个内存结构之外,Buffer Cache的管理还存在另外两个重要的数据结构:Hash Bucket和Cache Buffer Chain。

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

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

Bucket内部通过Cache Buffer Chain(Cache 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$BH与Buffer Header

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

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

SQL> select *

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

3from x$bh order by tch desc)

4where rownum<11;

ADDRTS#FILE#DBARFILDBABLKTCH

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

0430A6E80111498652

0430C9FC0117882142

0430A5A40117938142

0430C9FC0118202142

0430C9FC0117873107

0430C9FC0117889107

0430C9FC0118233107

0430C9FC55518444106

0430C9FC55518516106

0430C9FC0119017106

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

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

2from dba_extents e,

3(select *

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

5from x$bh order by tch desc)

6where rownum<11) b

7where e.relative_fno=b.dbarfil

8and e.block_id<=b.dbablk

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

OWNERSEGMENT_NAMESEGMENT_TYPE

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

SYSI_JOB_NEXTINDEX

SYSI_FILE#_BLOCK#INDEX

SYSC_FILE#_BLOCK#CLUSTER

SYSI_FILE#_BLOCK#INDEX

SYSC_USER#CLUSTER

SYSI_OBJ1INDEX

SYSOBJ$TABLE

SYSOBJ$TABLE

SYSC_FILE#_BLOCK#CLUSTER

SYSC_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 Latch。Latch是Oracle数据库内部提供的一种低级锁,通过串行机制保护共享内存不被并发更新/修改所损坏。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

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

ADDRNAMEGETSMISSESSPIN_GETS

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

031D3724 shared pool000

031D365C shared pool000

031D3594 shared pool000

031D34CC shared pool000

031D3404 shared pool000

031D333C shared pool000

031D3274 shared pool9071010

判断和解决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的方法已经不再推荐使用。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值