导读:本期聚焦于白鲨创作的《SQL数据库UndoLog是什么?详解事务回滚的实现机制与底层原理》,敬请观看详情。事务一旦执行失败,数据库是如何把数据恢复到修改之前的状态的?答案就藏在UndoLog里。UndoLog是InnoDB等存储引擎实现原子性的核心组件,它会在事务修改数据之前先记录一份反向操作日志,比如插入对应删除、更新对应旧值还原,一旦发生回滚就按日志逆向执行。本文从UndoLog的基本概念讲起,深入分析回滚段的组织结构、undo记录的几种类型,梳理undo与redo、MVCC之间的协作关系,并结合实际的更新与回滚流程演示日志的产生与使用过程,最后说明undo的清理时机以及长事务带来的隐患,帮助你彻底弄懂数据库事务回滚背后的实现细节。

提到数据库事务的ACID特性,大多数人第一反应是redo log保证了持久性,而原子性这个“要么全做、要么全不做”的承诺,主要靠的却是另一套日志系统——UndoLog。当一条UPDATE语句执行到一半事务失败,或者手动执行ROLLBACK时,数据库能够精准地把数据还原回事务开始前的样子,这背后正是UndoLog在工作。理解UndoLog不仅能帮你搞懂回滚机制,还是掌握MVCC多版本读、优化长事务问题的基础。

SQL数据库UndoLog是什么?详解事务回滚的实现机制与底层原理

UndoLog到底是什么

UndoLog,中文通常叫回滚日志或撤销日志,是InnoDB在事务修改数据之前额外写入的一组记录。它的核心思想非常朴素:在改动数据之前,先把“怎么撤销这次改动”的信息记下来。比如一个事务要把某行的name字段从“张三”改成“李四”,那么在真正修改之前,InnoDB会先写一条undo记录,内容包含旧值“张三”。一旦事务需要回滚,只需要读出这条undo记录,把“张三”写回去,数据就恢复原状了。

与redo log记录“做了什么”不同,undo log记录的是“如何撤销做过的事”,两者方向相反。redo log是物理逻辑式的正向日志,用来崩溃恢复时重做操作;undo log则是逻辑式的反向日志,用来把数据逆向还原。正因为undo是逻辑日志,回滚时执行的其实是反向SQL语义的操作:INSERT的undo是DELETE,DELETE的undo是INSERT,UPDATE的undo则是把旧值重新UPDATE回去。

undo log本身也存在共享表空间或独立的undo表空间文件中,由回滚段(Rollback Segment)统一管理。从MySQL 5.7开始支持独立undo表空间,8.0更是默认把undo log从系统表空间中剥离出来,这样可以显著降低系统表空间的膨胀压力。

回滚段与undo记录的组织结构

UndoLog并不是随意堆放的,InnoDB内部有一套层次分明的管理结构。最上层是回滚表空间(undo tablespace),每个表空间里包含128个回滚段(rollback segment),每个回滚段又划分为1024个undo slot,每个slot对应一个undo log链。当一个事务开始并且需要修改数据时,InnoDB会为它分配一个undo slot,事务的undo记录就以链表的形式串起来。

undo记录按操作类型分为三种。第一种是INSERT类型的undo,事务插入新行时记录主键值,回滚时按主键删除这行,这种undo在事务提交后就可以直接丢弃,因为新插入的数据对其他事务不可见,不需要它参与MVCC。第二种是UPDATE类型的undo,记录被修改列的旧值,回滚时用来还原,同时它也是构建旧版本数据链的关键,只有当没有任何事务需要依赖这个旧版本时才能被清理。第三种在一些实现里还会细分出DELETE操作对应的undo,本质上也记录整行旧数据用于重建。

每行数据记录的隐藏字段和undo链表配合,构成了多版本数据结构。InnoDB每行都有两个隐藏列:DB_TRX_ID记录最后修改该行的事务ID,DB_ROLL_PTR就是回滚指针,指向该行对应的undo记录。沿着DB_ROLL_PTR一路往undo链上追溯,就能把这一行历史上的各个版本全部还原出来,这就是MVCC快照读的实现基础。

-- 查看某行的隐藏字段效果(伪示意)
SELECT 
    id,
    name,
    DB_TRX_ID,      -- 最后修改事务ID
    DB_ROLL_PTR     -- 回滚指针,指向undo log
FROM user WHERE id = 1;

-- 事务回滚演示
START TRANSACTION;
UPDATE user SET name = '李四' WHERE id = 1;  -- 写undo:记录旧值'张三'
ROLLBACK;  -- 根据undo把name还原为'张三'
SELECT name FROM user WHERE id = 1;  -- 结果仍是'张三'

一次更新加回滚的完整流程

把前面的概念串起来,看一个完整的事务执行过程。假设事务T要更新一行数据,InnoDB的处理步骤大致是:首先检查目标行所在的页是否在缓冲池中,不在则从磁盘读入;接着在Buffer Pool中修改该行之前,先生成一条undo记录并写入undo页,这条记录包含旧值和回滚指针信息;然后在Buffer Pool中真正修改数据行,把该行的DB_TRX_ID改为T的事务ID,DB_ROLL_PTR指向刚写入的undo记录;最后把“修改了哪个页”的redo log(包括数据页和undo页的改动)写入log buffer。注意这里有个容易忽略的细节:undo log的写入本身也会产生redo log,因为undo页也是需要崩溃恢复保护的。

如果事务最终提交,INSERT类型的undo会被标记为可清理,UPDATE类型的undo则进入历史链表,等待purge线程回收。如果事务失败执行ROLLBACK,InnoDB会从该事务的undo链尾部开始,逐条读取undo记录并执行反向操作。回滚并不是瞬间完成的,一个修改了百万行的事务,回滚时同样要逐行还原,耗时可能与正向执行相当,这就是为什么大事务回滚经常在生产环境造成长时间阻塞。

undo与redo、MVCC的协作关系

单独看undo只是一份反向操作日志,但它的价值要在与其他机制的配合中体现。和redo log的关系可以概括为:redo保证undo log本身不丢,undo保证数据可以撤销,两者共同支撑起InnoDB的崩溃恢复能力。数据库宕机重启时,InnoDB先用redo把所有已提交和未提交的修改都重做到一个一致状态,再借助undo把未提交事务的修改全部回滚掉,最终呈现出“已提交的都在、未提交的都不在”的正确状态。

和MVCC的关系则更为紧密。普通SELECT走快照读时不需要加锁,它依据ReadView判断当前版本是否可见,如果不可见,就沿着DB_ROLL_PTR到undo链上找上一个版本继续判断,直到找到可见版本为止。也就是说,如果没有undo log保存的旧版本数据,MVCC的并发读根本无从谈起。反过来看,也正因为undo要为旧事务提供版本可见性支持,已提交事务的undo不能立即删除,必须等所有早于它的ReadView都失效之后,才能由purge线程统一清理。

  • redo log:记录正向物理修改,保证持久性,支持崩溃后重做
  • undo log:记录反向逻辑操作,保证原子性,支撑回滚与MVCC
  • purge线程:负责清理不再被任何快照引用的undo记录

长事务为什么是undo的天敌

理解了undo的清理条件,就明白了长事务的危害。假设某个事务开启后长时间不提交,或者一个查询执行了几个小时,期间它会持有一个很老的ReadView。只要这个ReadView存在,其后所有事务产生的undo记录都无法被purge清理,undo表空间就会持续膨胀,不仅占用磁盘,还会拖慢新事务申请undo slot以及MVCC沿版本链回溯的效率。

排查长事务可以直接查询information_schema中的事务视图,找到执行时间过长的事务并及时处理。在日常开发中,也应避免在事务里执行RPC调用、文件上传之类的耗时操作,尽量把事务拆小,用业务上的幂等设计替代“一个事务包打天下”的思路。

-- 查找运行时间超过60秒的事务
SELECT * FROM information_schema.innodb_trx
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;

-- 查看undo表空间使用情况
SELECT * FROM information_schema.innodb_metrics
WHERE NAME LIKE 'trx_rseg%';

总的来说,UndoLog是InnoDB实现原子性和多版本并发的基石。它以逻辑反向日志的形式记录每次修改的撤销方法,通过回滚段精细管理,与redo log配合完成崩溃恢复,与ReadView配合实现快照读。掌握它的原理,遇到回滚慢、undo膨胀、长事务锁等待这类问题时,才能从本质上定位原因,而不是盲目重启了事。

UndoLog事务回滚MySQL事务修改时间:2026-09-03 12:57:25

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49573.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。