MySQL Undo Log日志

29 阅读5分钟

Undo log在MySQL事务的实现中也起着至关重要的作用,MySQL中事务的一致性是由Undo Log实现的。Undo log在MySQL事务的实现中主要起到两方面的作用:回滚事务和多版本并发事务,也就是我们常说的MVCC机制。

1 基本概念

MySQL在启动事务之前,会先将要修改的数据记录存储到Undo Log中。如果数据库的事务回滚或者MySQL数据库崩溃,可以利用Undo Log对数据库中未提交的事务进行回滚操作,从而保证数据库中数据的一致性。

Undo Log会在事务开始前产生,当事务提交时,并不会立即删除响应的Undo Log。此时,InnoDB引擎会将当前事务对应的Undo Log放入待删除的列表,接下来,接下来,通过一个后台线程purge thread进行删除处理。

Undo Log和Redo Log不同,Undo Log记录的是逻辑日志,可以这样理解:当数据库执行一条insert语句时,Undo Log会记录一条对应的delete语句;当数据库执行一条Update语句时,Undo Log会记录一条相反的update语句。

需要注意的是,因为MySQL事务执行过程中产生的Undo Log也需要进行持久化操作,所以Undo Log也会产生Redo Log。由于Undo Log的完整性和可扩展性需要Redo Log来保证,因此数据库崩溃时需要先做Redo Log数据恢复,然后再做Undo Log回滚。

2 存储方式

 2.1 系统表空间存储(传统方式)

  • ​默认位置​​:早期版本中,Undo Log 默认存储在系统表空间 ibdata1 文件中
  • ​特点​​:
    • 所有 undo log 共享同一存储空间
    • 容易造成 ibdata1 文件过大且无法收缩
    • 长期运行后可能导致磁盘空间问题

2.2 独立 Undo 表空间(MySQL 5.6+)

通过innodb_undo_tablespaces变量可以将回滚段平均分配到多个文件中。innodb_undo_tablespaces变量默认是0,表示将回滚段全部写到同一个文件中。

需要注意的是,innodb_undo_tablespaces变量只能在停止MySQL服务的情况下进行修改,重启后生效,不过不建议修改这个值。

  • ​配置参数​​:
    innodb_undo_directory = /path/to/undo  # 指定存储目录
    innodb_undo_tablespaces = 4           # 设置undo表空间数量
    innodb_undo_logs = 128                # 设置undo log段数量
    
  • ​文件命名​​:默认为 undo001 到 undoN(N为配置的数量)
  • ​特点​​:
    • 每个表空间大小默认约10MB
    • 支持在线 truncate(收缩)
    • 避免系统表空间膨胀问题

2.3 Undo Log 物理结构

  • ​页结构​​:使用特殊的 undo 页(16KB大小)
  • ​段管理​​:
    • 每个事务使用独立的 undo 段
    • 系统维护回滚段(rollback segment),每个回滚段包含多个 undo slot
  • ​链式存储​​:同一记录的多次修改形成版本链

2.4 现代版本改进(MySQL 8.0+)

  • ​默认启用独立Undo表空间​
  • ​自动截断机制​​:
    innodb_undo_log_truncate = ON
    innodb_max_undo_log_size = 1G  # 触发truncate的阈值
    
  • ​临时Undo日志​​:MySQL 8.0引入,将临时表的undo日志与常规undo日志分离

2.5 存储内容格式

  • ​INSERT操作​​:存储主键信息,回滚时执行删除
  • ​DELETE操作​​:存储完整记录,回滚时执行插入
  • ​UPDATE操作​​:存储被修改前的内容,回滚时恢复旧值

2.6 空间回收机制

  • 事务提交后,undo log 不会立即删除
  • 当没有活跃事务需要访问旧版本时,空间可被回收
  • 通过后台 purge 线程清理不再需要的 undo log

3 MVCC机制与Undo Log的关系

3.1 MVCC 如何利用Undo Log

当执行快照读(普通 SELECT) 操作时,MVCC 会按照以下步骤来获取数据的适当版本:

  1. 确定事务的可见性:每个事务在启动时,都会生成一个一致性视图(consistent read view),这个视图包含了当前活跃事务的列表。
  2. 版本链追溯:从数据的当前版本开始,依据 DB_ROLL_PTR 指针不断追溯 undo log,直到找到满足可见性条件的版本。
  3. 判断版本是否可见:通过比较 DB_TRX_ID 和一致性视图,来判断该版本是否对当前事务可见。

3.2. 一个实际例子

假设有如下的操作流程:

  1. 事务 T1将数据从 A 修改为 B,此时会生成一条 undo log,记录原来的值 A。
  2. 事务 T2(在 T1 提交之后启动)将数据从 B 修改为 C,同时生成一条 undo log,记录原来的值 B。
  3. 事务 T3(在 T2 修改之后启动)执行快照读时:
    • 它首先读取到的数据是 C(当前版本),其 DB_TRX_ID 为 T2。
    • 如果 T2 在 T3 启动时还处于活跃状态,那么 T3 会通过 DB_ROLL_PTR 追溯到 undo log 中的 B 版本。
    • 如果 B 版本的 DB_TRX_ID 对 T3 可见,那么 T3 就会读取到 B 这个值。

3.3. 关键优势

  • 无锁读:通过 MVCC 和 undo log,SELECT 操作无需加锁,从而提高了并发性能。
  • 实现事务隔离:在 REPEATABLE READ 隔离级别下,一个事务在执行期间读取到的数据版本是一致的。
  • 回滚支持:undo log 能够保证事务可以原子性地回滚。