MySQ回滚日志Undo Log实践记录

 更新时间:2025年09月03日 11:34:35   作者:M_Reus_11  
Undo Log是一种用于撤销操作的日志,它记录了事务发生之前的数据状态(主要是修改前的旧版本数据),本文给大家介绍MySQ回滚日志Undo Log实践记录,感兴趣的朋友一起看看吧

Undo Log 是 InnoDB 存储引擎中实现事务关键组件之一。它与 Redo Log 共同协作,确保了事务的原子性(Atomicity)和一致性(Consistency),同时也是 MySQL 实现多版本并发控制(MVCC) 的基础。

一、什么是 Undo Log?

Undo Log,顾名思义,是一种用于撤销操作的日志。它记录了事务发生之前的数据状态(主要是修改前的旧版本数据)。

当执行一个 DELETEUPDATE 或 INSERT 操作时,InnoDB 不仅会生成 Redo Log 用于重做,还会生成相应的 Undo Log。如果事务需要回滚(ROLLBACK)或者系统崩溃后进行恢复,InnoDB 就可以利用 Undo Log 中的信息,将数据还原到修改前的状态。

核心思想: 在修改任何数据之前,先“留底”,把旧数据拷贝一份到 Undo Log 中。

二、Undo Log 的主要作用

  • 实现事务回滚(原子性)
  • 这是 Undo Log 最直接的作用。当一个事务执行失败或用户显式执行 ROLLBACK 时,InnoDB 会读取对应事务的 Undo Log,执行相反的逆操作来撤销更改:
    • 对于 INSERT,逆操作是 DELETE
    • 对于 DELETE,逆操作是 INSERT
    • 对于 UPDATE,逆操作是用旧值再 UPDATE 回去。
  • 实现多版本并发控制(MVCC)- 一致性读
  • 这是 Undo Log 在现代数据库中最重要、最高频的作用。MVCC 使得读操作(SELECT)不会阻塞写操作(UPDATE/DELETE),写操作也不会阻塞读操作
    • 当某个事务需要读取一行数据时,InnoDB 会找到该行数据的一个“可见”版本。
    • 如果该行数据的最新版本(由某个活跃事务修改)对当前读事务不可见,InnoDB 就会沿着该行记录的 DB_ROLL_PTR(回滚指针),在 Undo Log 中寻找更早的、符合当前事务隔离级别要求的旧版本数据。
    • 这些旧版本数据链(版本链)就存储在 Undo Log 中。因此,一个读请求可能会访问到很久之前的数据快照,这些快照就是通过 Undo Log 构建出来的。

三、Undo Log 的存储与结构

1. 物理存储

  • 存储位置: Undo Log 存储在表空间中。从 MySQL 5.6 开始,可以配置为使用独立的 Undo 表空间 (.ibu 文件),与系统表空间 (ibdata1) 分离,方便管理和扩展。
  • 回滚段 (Rollback Segments): InnoDB 有 128 个回滚段(Rollback Segments),其中:
    • 第 0 号、第 1 号、第 33-127 号回滚段存在于临时表空间。
    • 第 1-32 号回滚段存在于普通表空间(系统表空间或独立 Undo 表空间)。
    • 每个回滚段管理着多个 Undo Slot,每个 Slot 对应一个 Undo Log Segment。
  • Purge 机制: Undo Log 不会永远保留。当没有任何事务或快照读需要用到某个旧版本数据时(即该 Undo Log 不再被 MVCC 所需),这个 Undo Log 所占用的空间就可以被回收重用。这个删除过期 Undo Log 的过程由后台的 Purge 线程负责。

2. 逻辑结构 - 版本链

每一行记录(聚簇索引)在 InnoDB 中都包含两个隐藏字段:

  • DB_TRX_ID(6字节): 最近一次修改该行数据的事务 ID。
  • DB_ROLL_PTR(7字节): 回滚指针,指向该行数据的上一个旧版本在 Undo Log 中的位置。

UPDATE 操作会形成一个版本链:

  • 事务 A (Trx-id=100) 修改了某行数据。
  • 修改前,该行的旧数据(包括 DB_TRX_ID 和所有字段值)被拷贝到 Undo Log 中。
  • 修改后,新行的 DB_TRX_ID 被设置为 100,DB_ROLL_PTR 指向刚刚创建的 Undo Log 记录。
  • 当事务 B (Trx-id=200) 再次修改这行数据时,过程重复:拷贝当前状态到新的 Undo Log 记录,然后更新数据行,并将新的 DB_ROLL_PTR 指向事务 B 创建的 Undo Log 记录。

这样,通过 DB_ROLL_PTR,所有历史版本的数据就像一条链表一样被串联起来,这就是版本链

示例:
假设一行数据初始值为 Name=‘Alice’

  • 事务 100 将其改为 Name=‘Bob’
  • 事务 200 又将其改为 Name=‘Charlie’

这行记录及其版本链的结构如下:

当前行 (In Table)   : [Name='Charlie', DB_TRX_ID=200, DB_ROLL_PTR --> Undo Record 200]
                      ^
                      |
Undo Record 200      : [Name='Bob',    DB_TRX_ID=100, DB_ROLL_PTR --> Undo Record 100]
                      ^
                      |
Undo Record 100      : [Name='Alice',  DB_TRX_ID=?,   DB_ROLL_PTR -> NULL]

当有一个 Read View 需要查询这行数据时,它会从最新的记录开始,顺着 DB_ROLL_PTR 依次判断哪个版本对它可见。

四、Undo Log 与 Redo Log 的区别

这是一个非常重要的概念,两者的区别和联系如下表所示:

特性Redo LogUndo Log
目的重做日志,确保事务的持久性回滚日志,确保事务的原子性一致性读(MVCC)
内容记录的是数据页的物理变化(在某个页上做了什么修改)记录的是数据修改前的逻辑状态(行的旧值)
生成时机在事务执行过程中不断写入在数据修改生成
作用时机数据库崩溃恢复时,重放已提交的事务事务回滚时和一致性读(MVCC) 时
生命周期事务提交后,对应的 Redo Log 可能很快被覆盖(循环写)事务提交后,Undo Log 可能仍被 MVCC 使用,不能立即删除
磁盘存储顺序写入(ib_logfile0/1随机写入(存在于表空间)
日志类型物理逻辑日志(物理到页,逻辑到行)逻辑日志

关键联系: Undo Log 本身的操作(写入、修改)也会产生 Redo Log。因为 Undo Log 也需要持久化,防止在写入 Undo Log 过程中发生崩溃导致数据不一致。这被称为 “Redo Log for Undo Log”

五、相关参数与最佳实践

  • innodb_undo_tablespaces: 设置独立 Undo 表空间的个数。通常建议设置为 2 或更多,便于管理和空间回收。
  • innodb_max_undo_log_size: 指定每个 Undo 表空间文件的最大大小(默认为 1GB)。超过此值,表空间会被标记为可截断。
  • innodb_undo_log_truncate: 是否启用自动截断(收缩)Undo 表空间的功能。强烈建议开启(=ON),否则 Undo 表空间会无限增长。
  • innodb_purge_threads: Purge 线程的数量。在高并发写场景下,可以适当增加此值(如设置为 4)以加快过期 Undo Log 的清理速度。

最佳实践

  • 启用独立 Undo 表空间和自动截断,避免 ibdata1 文件无限膨胀。
  • 对于有大事务长事务的系统,需要特别关注 Undo Log 的增长。因为一个长时间未提交的事务会阻止 Purge 线程清理它之后产生的所有 Undo Log,可能导致 Undo 表空间急剧增长。
  • 监控 SHOW ENGINE INNODB STATUS\G 输出中的 TRANSACTIONS 部分,关注历史链表长度(History list length),它代表了未 Purge 的 Undo Log 页的数量。

总结

Undo Log 是 InnoDB 引擎的基石之一,它远不止是“回滚”那么简单。它的核心价值在于:

  • 保障原子性:为事务回滚提供基础。
  • 实现 MVCC:构建数据行的多版本,是实现非锁定读(快照读)、提升数据库并发性能的关键。

理解 Undo Log 的工作原理,对于深入掌握 MySQL 的事务机制、MVCC 以及进行性能调优和故障排查都至关重要。

到此这篇关于MySQ回滚日志Undo Log实践记录的文章就介绍到这了,更多相关MySQ回滚Undo Log内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql执行脚本导入表和数据后中文注释乱码的问题解决

    mysql执行脚本导入表和数据后中文注释乱码的问题解决

    本人在使用不同版本下进行操作时,就会出现中文乱码的问题,,例如我本地安装mysql8,服务器安装的是mysql5,然后本地连接服务器的mysql后,执行SQL脚本之后发现中文全部乱码,所以本文介绍了mysql执行脚本导入表和数据后中文注释乱码的问题解决,需要的朋友可以参考下
    2024-04-04
  • 从索引到架构的MySQL大表查询优化实战指南

    从索引到架构的MySQL大表查询优化实战指南

    在MySQL实际开发中,大表查询慢是最常见、最头疼的性能问题,本文将从索引优化、SQL优化、架构优化、配置优化四个维度出发,结合可复现的实战SQL、原理分析、避坑指南,给出一套全链路的大表查询优化方案,帮你把性能提升100倍以上
    2026-03-03
  • Mysql将字符串按照指定字符分割的正确方法

    Mysql将字符串按照指定字符分割的正确方法

    字符串分割是我们开发中经常会遇到的一个需求,下面这篇文章主要给大家介绍了关于Mysql将字符串按照指定字符分割的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • Docker搭建Mysql8的主从复制实例代码

    Docker搭建Mysql8的主从复制实例代码

    Docker是一种流行的容器化平台,可以用来轻松部署、扩展和管理各种应用程序,使用 Docker 部署 MySQL 8 的主从复制可以让你实现数据的备份和读写分离,提高数据库的可用性和性能,这篇文章主要介绍了Docker搭建Mysql8的主从复制,需要的朋友可以参考下
    2026-05-05
  • mysql实现将data文件直接导入数据库文件

    mysql实现将data文件直接导入数据库文件

    这篇文章主要介绍了mysql实现将data文件直接导入数据库文件问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 一条SQL更新语句的执行过程解析

    一条SQL更新语句的执行过程解析

    这篇文章主要介绍了一条SQL更新语句的执行过程解析,所以一条更新语句的执行流程又是怎样的呢?下面我们一起进入文章了解更多具体内容吧
    2022-05-05
  • 连接mysql报错——没有设置“usessl=false”的问题

    连接mysql报错——没有设置“usessl=false”的问题

    这篇文章主要介绍了连接mysql报错——没有设置“usessl=false”的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • MySQL创建、删除索引的操作代码

    MySQL创建、删除索引的操作代码

    本文给大家介绍MySQL创建、删除索引的操作代码,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2026-03-03
  • mysql8.0.2离线安装配置方法图文教程

    mysql8.0.2离线安装配置方法图文教程

    这篇文章主要为大家详细介绍了mysql8.0.2离线安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • mysql报错1267 - Illegal mix of collations问题的解决方法

    mysql报错1267 - Illegal mix of collations问题的解决方法

    这篇文章主要介绍了mysql报错1267 - Illegal mix of collations问题的解决方法,解决这个问题的方法是将两个字符集统一起来,文中介绍了好几种解决的办法,需要的朋友可以参考下
    2025-01-01

最新评论