ORACLE 和MYSQL redo 和 undo 的含义
一、概念定义
redo 和 undo 是“先写日志再写数据”这套机制(WAL,Write-Ahead Logging)的两个组成部分:
- redo(重做日志):记录“数据页被改成了什么”,按顺序追加写入,用于崩溃后前滚重放,把已经落盘的日志变更重新应用到数据上。
- undo(回滚日志):记录“改动之前的旧值”,用于事务回滚和一致性读(构造修改前的版本)。
- 日志回放:数据库启动或恢复时,把日志中的变更按顺序重新应用,使数据库从崩溃后的不一致状态恢复到一个事务一致的状态。
两家产品的定位差异:
| 维度 | Oracle | MySQL(InnoDB) |
|---|---|---|
| redo 归属 | 数据库内核层的联机重做日志 + 归档日志 | InnoDB 存储引擎层的物理日志 |
| undo 归属 | undo 表空间 / undo 段(回滚段) | undo 表空间 / 回滚段(rollback segment) |
| 额外日志 | 归档日志用于介质恢复 | 服务器层 binlog,与 redo 通过两阶段提交协调 |
| 回放典型场景 | 实例恢复、介质恢复 | 崩溃恢复、备份恢复 |
二、作用与适用场景
Oracle
- 实例恢复(Instance Recovery):实例异常终止后,确保 buffer cache 中已提交的变更不丢失,并回滚未提交事务,使数据库达到一致状态。
- 介质恢复(Media Recovery):数据文件损坏或丢失时,用备份 + 归档日志 + 联机日志前滚到目标时间点。
- 事务回滚与一致性读:ROLLBACK 按 undo 链逆向撤销;查询用 undo 构造读一致性版本。
MySQL
- 崩溃恢复:异常宕机后,用 redo 把已提交但未刷盘的数据页变更恢复到数据文件。
- 事务回滚与 MVCC:undo 用于回滚,也为一致性读提供旧版本;提交后由 purge 线程清理不再需要的 undo。
- 主从复制与备份:binlog 负责逻辑变更传播;redo 负责引擎层持久化,两者靠两阶段提交保持一致。
共同点:提交只保证日志落盘,不要求数据页已经落盘,所以崩溃后必须回放。
三、工作机制
1. Oracle 的回放机制
正常运行阶段
- 会话修改数据块,改动先在 buffer cache 中完成;
- 同时生成 redo 记录写入 log buffer,生成 undo 记录写入 undo 段;
- 提交时 LGWR 把 redo 从 log buffer 写入联机重做日志文件(提交等待即
log file sync); - 脏数据块由 DBWn 延迟写出,检查点控制推进节奏。
实例恢复阶段
- 确定起点:从控制文件中记录的检查点位置(RBA/SCN)开始;
- 前滚(roll forward):顺序应用 redo,把日志中记录的块变更重新做一遍。这一步包括未提交事务的变更,先把数据恢复成崩溃瞬间的样子;
- 回滚(roll back):用 undo 逆向撤销未提交事务,按 undo 链逐条回退;
- 完成 open:回滚完成后实例恢复才算真正结束。因此 redo 或 undo 任一丢失、损坏,都可能导致实例恢复失败;
- 介质恢复则在此基础上还需要归档日志补齐日志链。
要点:undo 本身的修改也会产生 redo,否则恢复时无法重建 undo。
2. MySQL(InnoDB)的回放机制
正常运行阶段
- 修改先发生在 buffer pool 的数据页上;
- 生成 redo 写入 redo log buffer,事务提交时按
innodb_flush_log_at_trx_commit的策略刷入 redo log file; - 同时生成 undo(分 insert undo 与 update undo 两类),写入 undo 表空间;
- 若开启 binlog,则执行两阶段提交:
- Prepare:redo 进入 prepare 状态;
- 写 binlog 并 fsync;
- Commit:redo 进入 commit 状态。
崩溃恢复阶段
- 确定起点:从最近一次 checkpoint 对应的 LSN 开始扫描 redo;
- 前滚:把 redo 重放到日志末尾的最新 LSN,恢复已提交但未刷盘的数据页变更;
- 处理 prepare 事务:对处于 prepare 状态的事务,用 XID 与 binlog 比对——binlog 中存在则提交,不存在则回滚,从而保证 redo 与 binlog 一致;
- 回滚未提交事务:用 undo 撤销;
- purge:后续由 purge 线程清理不再需要的 undo 版本。
要点:
- 恢复时间与 checkpoint LSN 到当前 LSN 的差距直接相关,差距越大需要重放越多;
- redo 日志文件较大能减少检查点频率、降低日常 I/O,但会拉长崩溃恢复时间;
- redo 归档文件(无论哪种归档方式)不参与崩溃恢复,崩溃恢复只读
#innodb_redo目录下的 redo 文件。
四、相关对象与观察入口
Oracle
| 视图/入口 | 用途 | 与本概念的关联 |
|---|---|---|
| V$LOG | 联机重做日志组状态 | 观察当前日志、切换与归档状态 |
| V$LOG_HISTORY | 控制文件中的日志历史 | 判断日志链与恢复起点 |
| V$INSTANCE_RECOVERY | 恢复 I/O 限制机制 | 结合 FAST_START_MTTR_TARGET 估算恢复量 |
| V$TRANSACTION | 活动事务 | 观察事务的 undo 占用 |
| V$ROLLSTAT | 回滚段统计 | 观察回滚段使用与回滚活动 |
| V$UNDOSTAT | undo 使用直方图(10 分钟一档) | 观察 undo 空间消耗与事务并发 |
其他入口:告警日志中的 “Instance Recovery”“Media Recovery” 记录;等待事件 log file sync、log file parallel write、log buffer space;统计项 redo size、redo entries 等。
MySQL
| 视图/入口 | 用途 | 与本概念的关联 |
|---|---|---|
SHOW ENGINE INNODB STATUS |
引擎运行状态 | LOG 段可见 Log sequence number、Last checkpoint at,直接反映待重放量 |
| information_schema.INNODB_TRX | 当前事务 | 观察事务状态与 undo 相关活动 |
| information_schema.INNODB_METRICS | InnoDB 计数器 | 观察 redo/undo 相关统计 |
| performance_schema | 细粒度等待与统计 | 分析日志写入与恢复相关等待 |
其他入口:#innodb_redo 目录下的 redo 文件;undo 表空间文件;参数 innodb_redo_log_capacity、innodb_log_file_size、innodb_flush_log_at_trx_commit、innodb_fast_shutdown、innodb_force_recovery;innodb_fast_shutdown=2 相当于冷关闭,下次启动会走崩溃恢复流程。
五、常见误区与边界
- redo 与 undo 不能互相替代:redo 负责前滚,undo 负责回退。redo 重放时也会重放未提交事务,最终一致性靠 undo 收尾。
- “回放”不等于“把日志应用到最新”:实例/崩溃恢复只回到崩溃点;时间点恢复受恢复目标限制。
- redo 不是 SQL 日志:Oracle redo 是块级变更记录,MySQL redo 是数据页级物理日志;MySQL 的语句/行级逻辑记录在 binlog 里,两者不要混为一谈。
- MySQL 的 binlog 与 redo 是两层日志:binlog 在服务器层,redo 在 InnoDB 层,靠两阶段提交保证一致;Oracle 没有同名的 binlog 机制,对应场景是归档日志与备库日志传输。
- 提交只保证日志落盘,不保证数据页落盘,所以崩溃后必须回放。
- undo 也会产生 redo:不能认为“写 undo 就没有日志开销”。
- PostgreSQL 等产品没有同名 undo 段,其 MVCC 由旧版本元组 + WAL 实现,不能直接套用本文术语。
- 日志损坏或缺链才会需要介质恢复;普通断电一般只走实例恢复/崩溃恢复,不需要备份介入。
六、延伸知识
- 检查点与恢复量的权衡:Oracle 用
FAST_START_MTTR_TARGET控制恢复时间;MySQL 用 checkpoint LSN 差距衡量,redo 文件越大恢复越慢。 - 日志序列定位:Oracle 用 SCN/RBA,MySQL 用 LSN,都是判断“从哪里开始重放”的基础。
- MVCC 与一致性读:Oracle 靠 undo 构造读一致性;MySQL 靠 undo + Read View。
- 清理机制:Oracle 由 SMON 管理 undo 段与
undo_retention;MySQL 由 purge 线程清理 undo,长事务会拖住 purge。 - 高可用延伸:Oracle Data Guard 的日志应用、MySQL 主从复制与 MGR 的日志传输,本质都是“日志回放”的跨实例延伸。
浙公网安备 33010602011771号