事务回滚通过Undo日志的版本链实现,利用DB_TRX_ID和DB_ROLL_PTR逆向追溯旧版本;多事务下结合Read View判断可见性;Purge线程异步清理不再需要的Undo记录。

事务回滚依赖Undo日志构建的版本链,核心是通过事务ID(TRX_ID)和回滚指针(ROLL_PTR)在Undo Log中逆向追溯旧版本数据。
Undo日志如何记录数据变更
每次UPDATE或DELETE操作,InnoDB会把原数据(变更前的行记录)写入Undo Log,并在当前聚簇索引记录中添加两个隐藏字段:
- DB_TRX_ID:记录最后一次修改该行的事务ID
- DB_ROLL_PTR:指向该行上一个版本在Undo Log中的位置(即回滚指针)
这些信息共同构成“版本链”,新版本指向旧版本,形成从最新到最老的单向链表。
事务回滚时如何定位并恢复旧版本
执行ROLLBACK时,系统并不真正“删除”新数据,而是沿DB_ROLL_PTR逐级查找,还原到事务开始前的状态:
- 从当前记录出发,用DB_ROLL_PTR读取Undo Log中前一版本
- 检查该版本的DB_TRX_ID是否属于本事务或更早未提交事务;若不可见,则继续向上追溯
- 直到找到事务启动快照(Read View)中可见的最早版本,或链尾(即INSERT前的NULL状态)
例如:事务T1修改了某行三次,对应产生三个Undo Log节点;回滚时直接沿链回到第一个Undo版本,即T1开始前的状态。
多事务并发下的版本可见性控制
Undo链本身不决定“该读哪个版本”,而是配合Read View判断可见性:
- 每个一致性读(如SELECT)会创建Read View,包含当时活跃事务ID列表、最小未分配事务ID(min_trx_id)、最大已提交事务ID(max_trx_id)
- 遍历版本链时,用DB_TRX_ID与Read View比对:若小于min_trx_id,表示已提交且可见;若在活跃列表中,表示未提交,不可见;否则按提交状态进一步判断
这使得不同事务即使访问同一行,也能看到各自隔离级别下正确的快照版本。
Undo Log的物理组织与清理时机
Undo Log并非无限增长,其管理由Purge线程异步完成:
- Undo页按段(Segment)组织,分为Insert Undo和Update/Delete Undo两类,后者因需支持MVCC而长期保留
- 只有当某个版本对所有活跃Read View都不可见(即没有事务再需要它)时,Purge线程才会真正删除对应Undo记录
- 如果长事务未结束,会阻碍Purge,导致Undo空间膨胀甚至占满ibdata文件
因此,避免长时间运行的事务,是保障Undo链高效、可控的关键实践。










