事务原子性说白了就一句话:
一个事务里的一连串操作,要么全部做完提交,要么中间哪一步翻车了,就全部退回原样,不能停在改了一半的状态。
InnoDB 主要依靠 undo log 实现事务回滚,从而保证事务操作的原子性。
但 undo log 到底记了什么、怎么记的,很多人只背得出"回滚日志"四个字,一问细节就虚。
undo log 记的到底是什么
undo log 记的不是"反向 SQL",而是恢复数据旧版本所需要的信息。对 UPDATE、DELETE 来说,其中最核心的是修改前的旧值,以及恢复旧版本所需的其他信息。
不少人以为是 MySQL 偷偷存了一条相反的 SQL,回滚时执行一遍就完事。
不是这样。
undo log 是逻辑日志,它记的是"怎么把这条数据变回操作前的样子",而不是数据页在磁盘上的物理变化。
举一个具体的例子。
假设有一行 id=1 的账户,余额本来是 1000。
你执行:
UPDATE user SET balance = 900 WHERE id = 1;
undo log 不会记"balance 最终是 1000"这种废话,也不会存一条 UPDATE user SET balance = 1000。
对这个 UPDATE 来说,undo log 中最核心的信息之一,就是修改前的旧值:balance = 1000。
此外,实际的 undo 记录还不止这一个值,它还会保存用于定位这条记录、把多个历史版本串起来的其他信息,这些就是 MVCC 能顺着版本链找到旧数据的基础。
为什么 undo log 不是一条"反向 SQL"
因为 InnoDB 的回滚面对的是底层数据记录,而不是重新执行一条 SQL。
undo log 直接保存恢复旧版本所需的信息,回滚时可以按照这些信息把记录恢复到修改前的状态,不需要重新解析、优化和执行一条反向 SQL。
类比一下:
undo log 像打游戏的悔棋存档。
每次落子前先存一份旧局面,真要反悔,拿存档还原就行,比"倒着走一遍操作"靠谱得多。

删数据,undo log 又怎么记
DELETE FROM user WHERE id = 1,undo log 会保存恢复这条记录所需要的旧数据,而不是存一条 INSERT SQL。
这种记录恢复信息的方式有一个很直接的好处:
回滚时可以直接依据 undo 信息恢复旧版本,不需要重新执行和解析反向 SQL,也不依赖数据页在磁盘上的具体物理布局。
三种写操作,undo log 记法各不同
| INSERT | 记录用于定位和撤销新插入记录的信息 | 删除本次插入的记录 |
| UPDATE | 保存恢复旧版本所需的信息 | 将记录恢复到修改前状态 |
| DELETE | 保存恢复被删除记录所需的旧数据 | 恢复删除前的记录 |
为什么 INSERT 的 undo 信息相对简单?
因为插入之前这条记录根本不存在,没有旧版本需要保存。
undo log 只需要记录撤销这次插入所需的信息,事务回滚时,把本次插入的记录删除即可。
UPDATE 的 undo log 会保存恢复旧版本所需要的信息,其中包括被修改字段的旧值以及用于定位和版本管理的必要信息。
可以简单理解为:
它不需要保存整行所有字段,而是保存"把这条记录恢复到修改前状态所需的信息"。
DELETE 的回滚需要重新恢复被删除的记录,因此需要保存恢复这条记录所需的旧数据。
因为 InnoDB 的 DELETE 通常不是立刻把聚簇索引记录物理删除,而是先做删除标记,并保留相应的 undo 信息。
等没有事务再需要这个历史版本后,Purge 才会最终清理。
undo log 到底存在哪
这不能简单按版本一刀切。
undo log 的存储位置和 MySQL 版本及配置有关。
早期版本主要使用 InnoDB 系统表空间保存 undo 信息;
从 MySQL 8.0 开始,默认使用独立的 undo tablespace 保存 undo log,同时也支持进一步配置和管理 undo tablespace。
事务提交了,undo log 为什么不能立刻删
undo log 不会无限涨下去。
事务一提交,很多人以为所有 undo log 都能立刻删了。
不对。
这些历史版本不会在事务提交后立即清掉,而是由 InnoDB 的 Purge 机制在后台异步清理。
为什么不立刻删?
因为部分 undo log 不只是回滚要用,MVCC 也要靠它访问历史版本。
一个事务提交之后,可能还有其他事务持有较早的 Read View,需要沿着版本链访问旧版本,因此这些历史版本不能立刻清理。
等这些旧版本已经不可能再被任何活跃事务访问后,InnoDB 的 Purge 后台线程才会异步清理不再需要的历史版本及相关 undo 信息,逐步释放空间。
类比一下:
undo log 像便利店的监控录像。
一笔交易做完(事务提交),录像不会立刻销毁,因为还有人(MVCC 读请求)要回看历史画面;
等没人看了,清洁工(Purge 线程)才逐步清掉,腾出空间。

最后把原子性串一遍
核心是在数据发生变化时,保存恢复旧版本所需要的信息。
INSERT、UPDATE、DELETE 由于原始状态不同,产生的 undo 信息和回滚方式也不同;
事务提交后,部分 undo 信息仍不能立即清理,因为 MVCC 可能还需要访问历史版本,最终由 InnoDB 的 Purge 机制清理不再需要的历史版本及相关 undo 信息。
小贴士
本文讲的是正常事务回滚,至于数据库异常宕机后的恢复,还需要 Redo Log 等机制参与,那属于持久性和崩溃恢复的范畴。



