第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的方法已经不再推荐使用。

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

  1. oracle 权限问题9017,[数据库]oracle学习笔记(一)用户管理_星空网

    oracle学习笔记(一)用户管理 2014-04-13 0 1 --oracle学习第一天 2 --连接 @后面连接数据库实例,具体连接到那个数据库 3 conn scott/tiger@MYORA ...

  2. oracle数据库修改写入状态,【学习笔记】Oracle oradebug 使用oradebug修改数据库SCN方法案例...

    天萃荷净 使用oradebug修改数据库scn,使用oradebug修改数据库scn的案例. 这里也做了两个测试,发现该功能确实很巧妙,通过修改内存中的scn值,然后写入控制文件和数据文件,实现修改s ...

  3. oracle查询当前归档scn_【学习笔记】Oracle数据库 查看归档日志存放的位置

    [学习笔记]Oracle数据库 查看归档日志存放的位置 时间:2016-10-21 19:19   来源:Oracle研究中心   作者:HTZ   点击: 次 天萃荷净 分享一篇关于查看Oracle ...

  4. oracle rac添加监听,【学习笔记】Oracle 10G RAC增加节点时手动注册监听服务的案例步骤...

    [学习笔记]Oracle 10G RAC增加节点时手动注册监听服务的案例步骤 时间:2016-10-22 22:53   来源:Oracle研究中心   作者:HTZ   点击: 次 天萃荷净 Ora ...

  5. Oracle中用system存数据,【学习笔记】Oracle表空间 数据存放system表空间影响数据库性能...

    天萃荷净 分享一篇,关于Oracle数据库system表空间研究,不能将用户数据存放在system表空间的原因 为什么不建议客户把业务数据存放到SYSTEM表空间中,一直想通过试验的数据来说明问题,今 ...

  6. oracle 下bdump占用大,【学习笔记】Oracle oradebug 利用oradebug释放被删除文件空间的案例...

    天萃荷净 利用oradebug释放被删除文件空间,运维DBA反映系统空间不足,由于trace跟踪文件占用,删除后不能释放空间,分析原因为oracle进程句柄占用 在很多时候,检查系统时候发现,由于某个 ...

  7. oracle dg 增加redo组,【学习笔记】Oracle Data Guard 修改dataguard主库redo组数和大小

    天萃荷净 运维DBA反映检查到Oracle DataGuard环境redo日志较小,总结一下修改dataguard主库redo组数和大小方法 在一个dg环境中,配置的是实时同步,需要增加主库的redo ...

  8. oracle中如何格式化输出,【学习笔记】数据库日期时间格式化输出案例

    天萃荷净 分享一篇关于开发DBA在工作中常用到的日期时间格式化输出方法案例,含:mysql.sql server.oracle 3种数据库日期时间格式化输出 1.mysql数据库时间格式化输出 DAT ...

  9. oracle复制另一个字段,【学习笔记】Oracle存储过程 表中列不同时动态复制表中数据到另一个表中...

    天萃荷净 分享一篇关于Oracle存储过程实现表之间数据复制功能.两表中列不同,动态的将一表中的数据复制到另一个表中案例 因为要用到回收站功能,删除一条记录,要先放到一个delete表中,以便以后恢复 ...

最新文章

  1. Windows原生运行Linux的技术细节
  2. MySQL递归查询父节点或递归查询子节点-陈远波
  3. 不装客户端连接mysql_C#不安装oracle客户端,如何连接到oracle数据库
  4. SSH框架应用中常用Jar包用途介绍
  5. 页面加载图片前用空态图代替真正图片
  6. php curl发送post请求失败,php 利用curl发送post请求
  7. 把网站图片和php程序分离,我的图片服务器和WEB应用服务器相分离的简单方案
  8. html标签和asp.net在GridView里面绑定js的区别
  9. 安卓rom制作教程_刹那 刷机教程201三星S10 N10等系列 韩版国行安卓9升级10 详细步骤...
  10. 3850x5服务器装系统,IBM X3850 X5服务器ESXi 5安装配置全过程——安装
  11. 尚硅谷2022 jenkins教程笔记
  12. JAVA后台管理系统
  13. J2EE6 servlet session超时机制
  14. VOC检测格式数据分析和处理
  15. 关于JAVA WEb如何连接Matlab
  16. Unity中零基础实现人物控制摇杆(下篇)
  17. 集合中某几个数字之和等于一个固定值 java
  18. 程序猿的十年—新猿农计划
  19. .net mvc 在 cshtml 中输出 html 格式问题
  20. 北京功略----购物篇

热门文章

  1. 在python中是没有NULL的,取而代之的是None,它的含义是为空
  2. 计算机远程桌面修复,让远程桌面管理恢复顺畅稳定
  3. centos 4.4 智能DNS解决南北互通方案
  4. 解决:springboot生成jar运行没有主清单属性
  5. 个人站长的疑问:怎么样才能做一个能赚钱的网站?
  6. 塞外风光,雁门雄关,古韵大同”塞北研学旅行团
  7. 单圆盘转子的临界转速和不平衡响应
  8. Android Studio键盘快捷键
  9. PYTHON 2.7爬虫获取豆瓣丑女的照片,备注信息稍后更新
  10. Android应用开发 00:Jetpack Compose学习 生日贺卡 图片 Compose象限 名片