Oracle 缓冲区缓存(Buffer Cache)的替换算法介绍

在这里插入图片描述
好的,我们将对 Oracle 数据库缓冲区缓存(Buffer Cache)的替换算法进行一场深入而全面的剖析。这是 Oracle 内存管理的核心,直接决定了数据库的性能表现。


第一部分:官方技术详解

一、核心概念与作用

1. 什么是缓冲区缓存?
缓冲区缓存是系统全局区(SGA)中最大且最关键的部分之一。它充当数据库数据文件(磁盘)和服务器进程(内存)之间的中间缓存层。其核心目的是通过在内存中保留最近和最频繁访问的数据块副本,来最大限度地减少昂贵且缓慢的物理 I/O 操作。

2. 为什么需要替换算法?
缓冲区缓存的大小是有限的,远小于整个数据库。当缓存已满且服务器进程需要将新数据块读入内存时,必须决定淘汰哪个现有的数据块,以便为新块腾出空间。这个决策机制就是替换算法。一个高效的算法应能尽可能多地保留“有用”的数据块,避免将 soon-to-be-used-again(很快再次被使用)的块换出,从而最大化缓存命中率。

二、内部架构与核心算法:LRU 与 TCH

Oracle 的缓冲区缓存管理是一个复杂的、自适应的系统,其核心是 LRU(Least Recently Used)算法 的一个高度优化变体,并引入了触摸次数(Touch Count, TCH) 的概念。

1. 缓冲区的状态与结构

缓冲区缓存中的每个数据块(Buffer)都有一个相关的缓冲区头(Buffer Header),它存储了丰富的元数据,用于管理其状态。这些头信息存储在高速缓存缓冲区链(CBC, Cache Buffers Chains) 上,并通过哈希表进行快速查找。

关键元数据包括:

  • 状态
    • Pinned:当前正被某个进程使用,不能被换出。
    • Clean:内存中的内容与磁盘一致。
    • Free/Unused:缓冲区为空,可供使用。
    • Dirty:内存中的内容已被修改,与磁盘不一致,最终必须由 DBWn(Database Writer) 进程写入磁盘。
  • 访问模式
    • 访问时间:最后一次被访问的时间戳。
    • 触摸次数(TCH):一个计数器,记录该块被访问的频率。
  • 队列指针:用于将缓冲区连接到各种管理链表(如 LRU 链表)。

2. 替换算法详解:多链表 LRU 与触摸次数(TCH)

Oracle 并不使用一个单一的 LRU 列表,而是使用两个主要链表来管理缓冲区:

  • LRU List(最近最少使用列表)

    • 这是一个按最近访问时间排序的链表。最近被访问的块被移动到链表的 MRU(Most Recently Used)端,而最久未被访问的块则滑向链表的 LRU 端
    • 当服务器进程需要空闲缓冲区(Free Buffer) 来读取新块时,它从 LRU 端的尾部开始扫描。
  • Dirty List(脏列表) / Write List

    • 专门用于链接所有脏缓冲区。DBWn 进程会定期或根据特定条件(如检查点)将此列表中的缓冲区写入磁盘。写入后,这些缓冲区被标记为 Clean,并可以重新链接到 LRU 列表上。

核心算法流程(“冷热”分离模型):

  1. 新块进入缓存

    • 当一个服务器进程需要读取一个不在缓存中的数据块时,它必须找到一个空闲缓冲区。
    • 它从 LRU 列表的尾部(LRU端) 开始扫描,寻找可用的候选者。
  2. 扫描与淘汰决策

    • 在扫描过程中,进程会检查每个遇到的缓冲区。
    • 如果缓冲区是干净的(Clean)且触摸次数(TCH)为 0:这是一个理想的淘汰候选。进程立即使用它来存放新读入的数据块。新块被插入到 LRU 列表的 MRU 端,并且其 TCH 被设置为 1。
    • 如果缓冲区是脏的(Dirty):进程不会立即处理它,而是跳过。脏缓冲区的写出由 DBWn 负责。
    • 如果缓冲区是干净的但 TCH > 0:这表示该块最近被访问过,是“热”块。进程不会淘汰它,而是将其 TCH 值减 1,并将其向 MRU 端移动一些位置。然后继续扫描。
  3. 触摸次数(TCH)的作用

    • TCH 是一个热度指标。每次缓冲区被访问(读或写),TCH 都会增加(有一个上限,通常是 10 左右)。
    • 这个机制确保了频繁访问的“热”块会保持较高的 TCH,从而在 LRU 扫描中很难被选中淘汰。而那些被访问一次后就再也没用过的“冷”块,其 TCH 会通过上述的减一操作逐渐降为 0,最终成为被淘汰的目标。
    • 这解决了传统 LRU 的一个弱点:偶尔的全表扫描会污染缓存,将大量热块挤出。因为全表扫描的块通常只被访问一次,它们的 TCH 很低,会很快被淘汰,而真正的热块由于 TCH 高,得以保留。
  4. 辅助列表(Auxiliary Lists)

    • 为了提升扫描效率,Oracle 还维护了其他辅助列表,如 LRUW(LRU Write,即脏列表)检查点队列(Checkpoint Queue),后者按块第一次变脏的日志顺序(RBA)链接脏缓冲区,用于高效的恢复和写出。
三、管理机制与相关参数
  • DB_CACHE_SIZE:定义标准块大小缓冲区缓存的大小。
  • DB_nK_CACHE_SIZE:为非标准块大小的表空间(如 4k, 16k, 32k)定义独立的缓存区域。
  • 多个缓冲池(Multiple Buffer Pools):允许对数据进行更精细的缓存策略管理。
    • KEEP Pool:用于存放绝对不能被换出的“钉死”对象(如小型的、频繁访问的代码表)。ALTER TABLE ... STORAGE (BUFFER_POOL KEEP);
    • RECYCLE Pool:用于存放一次性访问的大型对象(如临时作业的大表),防止它们污染主缓存。ALTER TABLE ... STORAGE (BUFFER_POOL RECYCLE);
    • DEFAULT Pool:默认池,使用上述的主替换算法。
  • 自动内存管理(AMM/ASMM)MEMORY_TARGETSGA_TARGET 参数允许 Oracle 自动调整缓冲区缓存的大小,根据工作负载在 SGA 各组件之间动态重新分配内存。

第二部分:场景、争用、排查与解决

四、性能争用与排查

缓冲区缓存的低效会直接导致物理 I/O 增加,从而引发一系列等待事件。

1. 场景:free buffer waits

  • 原理:这是最典型、最直接的缓冲区缓存争用事件。当服务器进程需要将新块读入缓存,但在 LRU 列表上找不到可用的干净缓冲区时,就会发生此等待。
  • 根本原因
    1. 缓存太小:无法容纳工作集(Working Set)。
    2. DBWn 写出速度跟不上:脏缓冲区产生得太快,DBWn 来不及将它们清洗干净,导致 LRU 列表上充斥着待写出的脏块,没有足够的干净块可供重用。
    3. 低效的 SQL:大量的全表扫描或低效的索引使用,产生了远超缓存容量的逻辑读,剧烈地冲刷缓存。
  • 排查
    1. 检查 AWR 报告的 “Buffer Pool” 部分。关注 Buffer Hit Ratio,但更重要的是 Free Buffer WaitsFree Buffer Inspected 的统计信息。Free Buffer Inspected 表示为了找一个空闲缓冲区而检查的块数,这个值过高意味着寻找空闲块非常困难。
    2. 检查 V$SYSSTAT
      SELECT name, value FROM v$sysstat
      WHERE name IN ('free buffer requested', 'free buffer inspected', 'dirty buffers inspected');
      
    3. 检查 DBWn 是否繁忙(V$BGPROCESS, AWR 报告的 “Background Wait Events”)。
  • 解决
    • 增加 DB_CACHE_SIZE:最直接的解决方案。
    • 优化 DBWn:增加 DB_WRITER_PROCESSES(多个 DBWn 进程),调整 DB_BLOCK_MAX_DIRTY_TARGET,或确保 I/O 子系统足够快。
    • 优化 SQL:这是治本的方法。减少不必要的逻辑读,增加索引以避免全表扫描。
    • 使用多缓冲池:将大型的、一次性的作业表分配到 RECYCLE 池,保护 DEFAULT 池中的热数据。

2. 场景:write complete waitsbuffer busy waits

  • write complete waits:当进程需要读取一个当前正被 DBWn 写出到磁盘的脏缓冲区时,它必须等待写出完成。这表明 I/O 或 DBWn 是瓶颈。
  • buffer busy waits:当多个进程同时请求以不兼容的模式(如一个要读,一个要修改)访问同一个缓冲区时发生。这通常是应用程序并发争用的表现(如索引块的热块争用、或者小表的频繁更新)。
  • 排查与解决
    • 查询 V$WAITSTAT 查看等待发生在什么类型的块上(如 data block, index block, undo block)。
      SELECT * FROM v$waitstat ORDER BY count DESC;
      
    • 对于 buffer busy waits on data blocks,考虑使用 自动段空间管理(ASSM) 或增加 PCTFREE 来分散数据。
    • 对于 buffer busy waits on index blocks,考虑使用反向键索引(Reverse Key Indexes) 或哈希分区索引来打散热点。
五、常用查询与管理 SQL
  • 查看缓冲区缓存命中率(历史):应结合 AWR 报告看趋势,单点值意义不大。

    SELECT (1 - (phy.value / (cur.value + con.value))) * 100 "Buffer Hit Ratio (%)"
    FROM v$sysstat cur, v$sysstat con, v$sysstat phy
    WHERE cur.name = 'db block gets'
      AND con.name = 'consistent gets'
      AND phy.name = 'physical reads';
    
  • 查看缓冲池配置和统计信息

    SELECT name, block_size, current_size, buffers, target_size
    FROM v$buffer_pool_statistics;
    
  • 查看对象在缓存中的热度

    SELECT o.owner, o.object_name, o.object_type,
           COUNT(*) buffers,
           SUM(DECODE(bh.STATUS, 'free', 0, 1)) used_buffers
    FROM dba_objects o, v$bh bh
    WHERE o.data_object_id = bh.objd
      AND o.owner NOT IN ('SYS','SYSTEM')
    GROUP BY o.owner, o.object_name, o.object_type
    ORDER BY used_buffers DESC;
    
  • 检查等待事件

    SELECT event, total_waits, time_waited, average_wait
    FROM v$system_event
    WHERE event IN ('free buffer waits', 'write complete waits', 'buffer busy waits', 'db file sequential read', 'db file scattered read')
    ORDER BY time_waited DESC;
    
  • 强制清空缓存(仅用于开发和测试!)

    ALTER SYSTEM FLUSH BUFFER_CACHE; -- 清空Buffer Cache
    ALTER SYSTEM FLUSH SHARED_POOL; -- 通常一并执行,清除执行计划
    

第三部分:通俗易懂的解释

想象一下缓冲区缓存是一个大学的图书馆阅览室,里面的座位(Buffers)是有限的。

  • 数据块:就是一本本的
  • 物理 I/O:就是去遥远的总书库调书,非常慢。
  • 目标:让最受欢迎、最近最常被借阅的书尽量留在阅览室里,减少去总书库的次数。

替换算法(图书馆管理员的管理规则):

  1. 新书到来:当一个学生(服务器进程)想读一本新书(数据块),但阅览室没座位了,他必须请走一个人,腾出座位。

  2. 寻找可清退的人

    • 管理员(替换算法)不会随便赶人。他会从最久没翻过书的人(LRU 端) 开始询问。
    • 他拍了拍第一个人:“同学,你的书(缓冲区)看完了吗(Clean)?而且我看你借阅记录(TCH)也很久没更新了(TCH=0)。” 如果是,太好了,就请他离开,让新同学坐下。新同学被安排到“刚来的人”区域(MRU 端),并得到一张积分卡(TCH=1)。
    • 如果他看到一个人书没看完(Dirty),他不会打扰他(因为需要先还书,这是 DBWn 的工作),直接跳过。
    • 如果他看到一个人正在认真看书,而且积分卡(TCH)分数很高,说明他是常客。管理员不会赶他走,反而会给他的积分卡扣一分(TCH-1),并把他往“刚来的人”区域挪一挪,然后继续往后找。
  3. 积分卡(TCH)的作用

    • 每次你续借或者打开书看,积分就加一分(上限10分)。这保证了真正的学霸(热块) 永远积分很高,很难被请走。
    • 而那种偶尔来一次、借一本畅销小说看两眼就放下(全表扫描) 的人,积分很快就会被扣光,成为首先被清退的目标。这样就保护了学霸的座位不被临时访客挤占。

出现的问题(等待事件):

  • free buffer waits(找不到座位等待):管理员发现,从一头走到另一头,每个人都在看书(全是脏缓冲区或热块),找不到一个可以清退的人。新同学只能干等着(等待),直到有一个人看完书(DBWn 把脏块写出)或者管理员终于找到一个可清退的人。

    • 原因:要么阅览室太小(缓存太小),要么大家看书时间太长(DBWn 写出慢),要么突然来了太多新同学(高逻辑读)。
  • buffer busy waits(争抢同一本书):两个学生都想看同一本书的同一页(同一个缓冲区),而且一个人想写读书笔记(修改),另一个人只想读。他们必须互相协调,一个人用的时候另一个人就得等着。

    • 原因:热门资源(热点块)的并发访问冲突。

总结
Oracle 的缓冲区缓存替换算法是一个精巧的、自适应的系统,它通过 LRU 链表触摸次数(TCH) 机制,智能地区分了“热数据”和“冷数据”,高效地管理着有限的内存资源。DBA 的核心工作就是确保这个“阅览室”:

  1. 足够大DB_CACHE_SIZE)。
  2. 管理员(DBWn)效率高(配置合理的 DBWR 进程和 I/O)。
  3. 来的学生(SQL)都是好学生,不会毫无节制地一次借走几百本书(优化 SQL,减少逻辑读)。

通过监控相关的等待事件和使用性能视图,DBA 可以精准地诊断出缓存系统的瓶颈所在,并采取相应的优化措施,从而保证数据库的响应速度。

欢迎关注我的公众号《IT小Chen

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值