当前位置: 首页 > news >正文

Oracle体系结构-Redo Log Buffer详解

1. 存在意义与核心功能 (Purpose & Core Function)

  • 核心目标: 保证数据库的**持久性 (Durability)**。即一旦一个事务被提交(COMMIT),它对数据库所做的更改就必须是永久性的,即使随后发生系统故障(如断电、崩溃)。
  • 问题根源: 直接将对数据块的修改(脏块)立即写入数据文件是极其低效的(随机 I/O 慢)。
  • 解决方案: Redo Log Buffer 应运而生。它的核心功能是:
    • 高速缓存 Redo 记录: 作为内存中的缓冲区,临时存放由数据库服务器进程生成的 **Redo 条目 (Redo Entries / Redo Records / Change Vectors)**。
    • 提升事务效率: 允许事务在提交时不必等待其对应的 Redo 记录物理写入磁盘(Redo Log Files),只需写入内存中的 Buffer 即可(非常快),从而极大提高了事务处理的吞吐量和响应速度。
    • 支持崩溃恢复: 在实例崩溃后重启时,Oracle 使用磁盘上的 Redo Log Files 来前滚 (Roll Forward) 所有已提交但尚未写入数据文件的更改,以及部分未提交但已记录的操作(后续再回滚),确保数据文件恢复到崩溃前的一致状态。Redo Log Buffer 是生成这些关键恢复信息的临时中转站。

2. 设计原理 (Design Principles)

  • 内存缓冲区: 位于 SGA (System Global Area) 中,是一块共享的、易失性的(Volatile)内存区域。这意味着实例关闭或崩溃时,其内容会丢失。
  • 循环结构 (Circular Buffer): 逻辑上被组织成一个环状队列。新产生的 Redo 记录被追加到 Buffer 的末尾。后台进程 LGWR (Log Writer) 负责将 Buffer 中从起始点到当前写入点的连续内容写入磁盘上的 Redo Log Files。一旦写入完成,这部分 Buffer 空间就可以被新产生的 Redo 记录覆盖重用。这种设计避免了频繁的内存分配/释放开销。
  • 大小固定: 其大小由初始化参数 LOG_BUFFER 决定。这是一个静态参数,只能在实例启动前修改(修改 SPFILEPFILE 后重启生效)。一旦实例启动,Buffer 大小就固定了。
  • 共享性: 所有服务器进程在修改数据块(产生 Redo)时,都需要将生成的 Redo 条目写入这个共享的 Redo Log Buffer。因此,并发访问需要协调(锁机制)。

3. Redo 条目的产生与内容 (Redo Record Generation & Content)

  • 触发时机: 当服务器进程执行 DML 语句 (INSERT, UPDATE, DELETE, MERGE)、DDL 语句(如 CREATE, ALTER, DROP) 或某些内部操作(如索引维护、事务管理)导致数据块发生变化时。
  • 产生者: 执行修改操作的服务器进程 (Server Process) 负责生成描述该变更的 Redo 条目。
  • 内容: Redo 条目包含了重建该变更所需的最少信息,主要包括:
    • 更改的操作类型(插入、更新、删除等)。
    • 更改发生的数据块地址(数据文件号、块号)。
    • 更改前的数据值(用于回滚 - Undo 生成也依赖 Redo)。
    • 更改后的数据值。
    • 事务标识符(SCN - System Change Number, Transaction ID)。
    • 关键点: Redo 记录的是物理变更(对哪些字节做了修改),而不是逻辑 SQL 语句本身。它是面向块(Block-Oriented)的变更描述。

4. 写入 Redo Log Buffer 的机制 (Writing to the Buffer)

  1. 服务器进程工作:
    • 服务器进程修改数据块前,需要生成对应的 Redo 条目。
    • 进程需要**获取 Redo Allocation Latch 或 Private Strand Latch (10g+)**。Latch 是一种轻量级的、短时间的锁,用于保护对 Buffer 中空闲空间的分配。
    • 获取 Latch 成功后,进程在 Buffer 中分配一块连续的空间(大小等于要写入的 Redo 条目大小)。
    • 进程将生成的 Redo 条目复制到分配到的 Buffer 空间。
    • 释放 Latch。
  2. 并发控制: 多个进程并发写入 Buffer 时,Latch 机制确保同一时刻只有一个进程在分配空间和写入其条目(对于同一个 Latch 保护的区域)。这是 Redo Log Buffer 可能成为并发瓶颈的原因之一(Latch 争用)。
  3. Copying vs. Pointers: 服务器进程是将其生成的 Redo 条目完整复制到 Buffer 中,而不是仅仅写入一个指针。这确保了 LGWR 进程可以直接从 Buffer 读取连续的 Redo 数据进行写入,无需额外的内存寻址开销。

5. Redo Log Buffer 的内部管理 (Internal Management - Strands)

  • 传统问题 (Pre-10g): 早期版本,所有服务器进程写入一个大的、共享的 Redo Log Buffer。高并发下,争抢单一的 redo allocation latch 成为严重瓶颈 (latch: redo allocation 等待事件)。
  • 解决方案: 从 Oracle 10g 开始,引入了 Private Redo Strands 的概念。
    • 原理: Redo Log Buffer 在逻辑上被划分为多个较小的、独立的区域,称为 Strands
    • 公共 Strand: 保留一个较小的公共区域(Public Strand)。
    • 私有 Strands: 大部分 Buffer 空间被划分为多个 Private Strands。每个 Private Strand 有自己的 Latch(redo allocation latch)。
    • 工作方式:
      • 当一个事务开始时,它会被动态地绑定到一个可用的 Private Strand。
      • 该事务产生的所有 Redo 条目都写入其绑定的 Private Strand。
      • 只有当事务绑定失败(所有 Private Strands 都忙)或事务非常大时,Redo 才会写入公共 Strand。
    • 优势:
      • 显著减少 Latch 争用: 多个事务可以并发地向不同的 Private Strands 写入,因为它们使用不同的 Latches。
      • 提高并行度: 多个 LGWR 进程(如果配置了)或 LGWR 可以并行处理多个已满的 Strands 的写入。
      • 更好的可伸缩性: 适应更高的并发事务负载。
    • 自动管理: Strands 的数量和大小主要由 Oracle 内部自动管理,通常与 CPU 数量相关。参数 _log_private_parallelism_log_private_memory 可影响其行为,但一般不建议修改。

6. Redo Log Buffer 的写出 (Flushing to Disk by LGWR)

  • 关键进程: LGWR (Log Writer Process) 是负责将 Redo Log Buffer 中的内容安全地、连续地写入到磁盘上的 Online Redo Log Files 的后台进程。
  • 触发 LGWR 写入的条件 (何时写):
    • 提交时 (COMMIT): 这是最常见且最重要的触发点。当用户执行 COMMIT 时,触发 LGWR 将包含该事务提交记录的 Redo 记录(以及该事务相关的所有 Redo)写入磁盘。只有 LGWR 成功将包含 COMMIT 记录的 Redo 写入磁盘后,COMMIT 操作才算真正完成,客户端才会收到“提交成功”的确认。这保证了持久性。
    • Buffer 达到 1/3 满: 防止 Buffer 被过快填满。
    • 每 3 秒: 即使 Buffer 未满或没有提交,LGWR 也会每隔大约 3 秒执行一次写操作(时间不完全精确),避免 Redo 数据在内存中停留过久。
    • DBWn 写入前 (Before DBWn Writes): 在 DBWn (Database Writer) 进程将脏缓冲区 (Dirty Buffer) 写入数据文件之前,它需要通知 LGWR 先将保护这些脏块的 Redo 记录写入磁盘(Write-Ahead Logging - WAL 协议)。这是保证崩溃恢复能正确重做更改的关键规则。如果 DBWn 要写的块对应的 Redo 还没落盘,DBWn 会触发 LGWR 先写。
    • 切换 Log File: 在日志切换(ALTER SYSTEM SWITCH LOGFILE)时,LGWR 必须将当前 Buffer 中的所有内容写入当前日志组,然后才能切换到下一组。
  • 写入方式:
    • LGWR 总是写入 Buffer 中连续的、尚未写入磁盘的部分(从上次写入点开始到当前写入位置)。
    • 它进行的是顺序的、大块的 I/O 操作(通常每次写大小为操作系统块大小的倍数,如 512KB 或 1MB),这比 DBWn 的随机 I/O 高效得多。
    • LGWR 将 Redo 写入当前活跃的 Online Redo Log Group 的成员文件。如果配置了多路复用(Multiplexing),LGWR 会同时写入组内的所有成员文件(理想情况下应在不同的物理磁盘上)。
    • 写入完成: LGWR 成功写入后,会更新 Buffer 的控制信息,标记已写入部分的空间为可重用。

7. 关键性能指标与等待事件 (Key Metrics & Wait Events)

  • LOG_BUFFER 大小: 参数值本身。太小可能导致频繁的 LGWR 写入和空间等待。
  • 等待事件 (Wait Events):
    • log buffer space 最重要的等待事件之一。表示服务器进程在尝试写入 Redo Log Buffer 时,发现 Buffer 中没有足够的空闲空间容纳其 Redo 条目。它必须等待 LGWR 将部分 Buffer 内容写入磁盘以释放空间后才能继续。这是 LOG_BUFFER 设置过小或 LGWR 写入不够及时(I/O 慢)的典型信号。
    • log file sync 表示服务器进程在提交事务 (COMMIT) 后,正在等待 LGWR 进程成功将包含该提交记录的 Redo 写入磁盘。这个等待时间直接代表了用户感知的 COMMIT 响应时间。过长的等待通常由慢的 Redo Log I/O(慢磁盘、争用)或 LGWR 过于繁忙引起。
    • latch: redo allocation / latch: redo copy 表示服务器进程在尝试获取写入 Redo Log Buffer 所需的 Latch 时发生争用(在非 Private Strands 主导的情况下或 Public Strand 争用时)。在 10g+ 的 Private Strands 架构下,这类争用通常显著减少。
  • 统计信息:
    • redo entries: 产生的 Redo 条目总数。
    • redo size: 产生的 Redo 数据总量(字节)。
    • redo buffer allocation retries: 服务器进程尝试分配 Redo Buffer 空间失败的次数(需要重试)。高值暗示 Buffer 空间紧张或 Latch 争用。
    • redo writes / redo blocks written: LGWR 写操作的次数和写入的块数。
    • redo write time: LGWR 花费在写操作上的总时间(厘秒)。
    • redo wastage: 由于日志切换等原因导致未能写入文件的 Redo Buffer 内容(浪费的空间)。通常很小。

8. 调优考量 (Tuning Considerations)

  • 设置 LOG_BUFFER 大小:
    • 原则: 足够大以平滑 Redo 的产生速率,避免频繁的 log buffer space 等待;但也不宜过大,因为过大的 Buffer 在实例崩溃时会丢失更多未写入磁盘的数据(尽管已提交事务的持久性由 COMMIT 触发 LGWR 保证)。
    • 判断依据:
      • 监控 log buffer space 等待事件:如果频繁出现且等待时间长,应考虑增大 LOG_BUFFER
      • 监控 redo buffer allocation retries: 高值也提示可能需要增大 Buffer。
      • 系统负载: 高并发、高 DML 系统通常需要更大的 Buffer。
      • 经验法则: 通常建议设置在 1MB 到 几十 MB 之间。现代系统常见设置为 16MB, 32MB, 64MB, 128MB 甚至更高。避免设置过小(如默认的几百 KB)。具体值需根据实际负载测试和监控确定。
  • 优化 LGWR 性能:
    • 关键! 这是缓解 log file sync 等待的核心。
    • 高速 I/O 子系统: 将 Redo Log Files 放在最快、最低延迟的磁盘上(如高性能 SSD/NVMe),并确保 I/O 路径无瓶颈。这是最重要的优化点。
    • 专用磁盘: 避免 Redo Log Files 与其他高 I/O 文件(数据文件、归档日志、Swap 空间)共享同一物理磁盘。
    • 多路复用 (Multiplexing): 使用多组成员文件(至少 2 组),并物理分散在不同磁盘/控制器上,提高可用性和可能的分担写负载(但 LGWR 是单进程写所有成员,主要提升的是冗余)。
    • 合理大小的 Redo Log Files: 过小的日志文件会导致频繁的日志切换(log file switch),而日志切换也会触发 Checkpoint(由 CKPT 触发 DBWn 写脏块),可能带来性能抖动。文件大小应能容纳一段合理时间(如 15-30 分钟)内产生的 Redo。监控 log file parallel write 时间和日志切换频率。
  • 减少不必要的 Redo:
    • 使用 NOLOGGING 操作(如直接路径加载 /*+ APPEND */,重建索引 NOLOGGINGSQL*Loader Direct Path)。需谨慎! 这会牺牲可恢复性,通常只用于可完全重新生成的数据加载场景。
    • 批量操作代替逐行提交。
  • 监控与诊断:
    • 定期检查上述关键等待事件 (log buffer space, log file sync, latch 相关)。
    • 使用 AWR, ASH 报告分析数据库整体性能,关注 Redo 相关指标。
    • 使用 V$SYSSTAT, V$SESSION_WAIT, V$LATCH 等动态性能视图进行实时监控。

9. 与相关组件的交互 (Interaction with Other Components)

  • Undo: 生成 Undo 数据(用于回滚和一致性读)的操作本身也会产生 Redo 记录(用于保护 Undo 数据)。因此,Redo Log Buffer 也间接保护了 Undo 信息的持久性。
  • DBWn (Database Writer): 严格遵守 WAL 协议。DBWn 写脏块前,对应的 Redo 必须已由 LGWR 写入磁盘。
  • CKPT (Checkpoint Process): 检查点发生时,CKPT 会更新控制文件和数据文件头的信息,记录当前恢复所需的起点(SCN)。这通常发生在日志切换时。检查点不直接写 Redo Buffer,但会促使 DBWn 写脏块,进而可能间接触发 LGWR。
  • ARCn (Archiver Process): 在归档模式下 (ARCHIVELOG),当在线日志文件写满切换后,ARCn 进程会将其复制到归档日志位置。归档日志是进行时间点恢复和搭建备库的基础。Redo Log Buffer 的内容最终会流向归档日志。
  • Commit Processing: COMMIT 的核心操作就是触发 LGWR 写包含该事务提交记录的 Redo。这是保证事务持久性的原子操作。
  • Crash Recovery: 实例崩溃后重启时,SMON (System Monitor) 进程利用磁盘上的 Redo Log Files(其内容最初来自 Redo Log Buffer)进行前滚 (Roll Forward) 操作,重做所有已提交但未落盘的更改。

总结提炼 (Summary)

  1. 定位: SGA 中的共享内存缓冲区。
  2. 核心作用: 高速缓存 Redo 条目,提升事务效率(延迟写盘),是实现持久性(Durability)和崩溃恢复(Crash Recovery)的基础。
  3. 内容: 物理变更记录(Redo Entries / Change Vectors),由服务器进程生成。
  4. 关键特性: 循环结构、固定大小 (LOG_BUFFER)、共享访问(需 Latch)。
  5. 写入者: 服务器进程(生成并复制 Redo 条目)。
  6. 写出者: LGWR 进程(核心后台进程)。
  7. 写出时机: COMMIT, Buffer 1/3满, 约3秒间隔, DBWn写前, 日志切换。
  8. 优化核心:
    • 合理设置 LOG_BUFFER (避免 log buffer space)。
    • 最大化 LGWR I/O 性能 (高速专用磁盘,解决 log file sync)。
    • 利用 Private Strands (减少 Latch 争用)。
    • 谨慎使用 NOLOGGING
  9. 核心等待事件: log buffer space (Buffer 太小/写慢), log file sync (LGWR I/O 慢/忙)。
  10. 设计哲学:顺序 I/O 换随机 I/O,用内存换速度,严格遵循 Write-Ahead Logging (WAL) 协议保证数据一致性和持久性。

理解 Redo Log Buffer 是深入掌握 Oracle 事务处理、恢复机制和高性能数据库调优的关键基石。它完美体现了数据库设计中权衡效率与可靠性的智慧。

http://www.xdnf.cn/news/1475245.html

相关文章:

  • Day22_【机器学习—集成学习(3)—Boosting—Adaboost算法】
  • FreeMarker快速入门指南
  • Lua 面向对象编程
  • 【MFC】对话框节点属性:Language(语言)
  • macOS下arm编译缺少stdint.h等问题
  • Python入门:从Hello World到项目创建
  • MySQL与ES索引区别
  • 【LeetCode热题100道笔记】二叉树的右视图
  • 数据结构中排序的时间、空间复杂度以及稳定性
  • 20250906-01:开始创建LangChain的第一个项目
  • 虚拟化技术
  • 文件I/O与I/O多路复用
  • 外置flash提示音打包脚本
  • 版本发布流程手册:Release分支规范与Bug分级标准全解析
  • [C++刷怪笼]:搜索二叉树--便利的查找工具
  • 【数据库相关】TxSQL新增数据库节点步骤
  • Nmap使用手册
  • 第08章 聚合函数
  • 数据结构:查找
  • Matplotlib 动态显示详解:技术深度与创新思考
  • 【3D算法技术】blender中,在曲面上如何进行贴图?
  • 少儿舞蹈小程序(9)校区信息展示
  • MAZANOKE与cpolar:打造安全可控的照片云端管理系统
  • 01-线上问题处理-树形结构拼接
  • 数据库原理及应用_数据库管理和保护_第5章数据库的安全性_理论部分
  • [光学原理与应用-436]:晶体光学 - 各向同性与各向异性是描述材料物理性质随方向变化特性
  • STAR-CCM+|雷诺数回顾
  • windows11 安装charm成功
  • U-Boot 多 CPU 执行状态引导
  • 【LeetCode热题100道笔记】验证二叉搜索树