中 进阶
undo-log与事务回滚#
一句话答案#
undo log 记录数据修改前的值,用于事务回滚(INSERT→DELETE)和 MVCC 版本链的快照读。
核心要点
| 日志 | 所属层 | 作用 | 格式 | 写入时机 |
|---|---|---|---|---|
| redo log | InnoDB 引擎层 | 崩溃恢复(保证持久性) | 物理日志(记录数据页的物理修改) | 事务执行过程中持续写 |
| undo log | InnoDB 引擎层 | 事务回滚 + MVCC 版本链 | 逻辑日志(记录逆操作) | 事务执行过程中持续写 |
| binlog | MySQL Server 层 | 主从复制 + 数据归档恢复 | 逻辑日志(SQL语句或行变更) | 事务提交时一次性写 |
关键区别:
- redo log 是循环写(固定大小,写满后从头覆盖),binlog 是追加写(可保留完整历史)
- redo log 是 InnoDB 引擎私有,binlog 是 MySQL Server 层,所有引擎共享
- redo log 用于实例恢复(宕机后恢复内存数据),binlog 用于数据恢复(时间点恢复)和主从同步
面试回答(2分钟版)
undo log 是 InnoDB 引擎层的逻辑日志,记录数据修改前的逆操作:INSERT 记一条 DELETE,UPDATE 记旧值。它有两个核心作用:第一是保证事务原子性,事务回滚时按 undo log 执行逆操作恢复原始数据;第二是支撑 MVCC,多个版本的 undo log 串成版本链,快照读通过 ReadView 沿版本链找到自己可见的数据版本,实现非锁定读。和 redo log 的区别在于:redo log 是物理日志用于崩溃恢复保证持久性,undo log 是逻辑日志用于回滚和多版本读保证原子性和隔离性。需要注意的是 undo log 不会在事务提交后立即删除,因为其他事务的快照读可能还需要访问这些旧版本,要等到没有活跃事务引用时才由 purge 线程清理。这也是长事务有害的原因——它会导致 undo log 持续堆积无法回收,占用大量存储空间。
追问与易错
追问方向:
- “undo log 什么时候被清理?”→ 事务提交后不会立即清理,要等到没有任何活跃事务的 Read View 引用该版本时,由 InnoDB 的 purge 线程异步回收
- “长事务为什么有害?”→ 长事务持有的 Read View 导致 undo log 版本链无法被 purge 清理,持续堆积占用大量存储;同时长时间持有行锁增加锁冲突和死锁概率
- “undo log 和 MVCC 什么关系?”→ undo log 通过 roll_pointer 串成版本链,MVCC 的快照读沿版本链查找,结合 Read View 的可见性规则找到当前事务应该看到的数据版本,实现非锁定一致性读
易错点:
- ❌ undo log 就是备份——是逻辑逆操作日志
- ❌ 事务提交后 undo 立即删——其他事务可能还在读