欢迎光临
我们一直在努力

MySQL 事务原子性:undo log 记什么、存哪、何时清

事务原子性说白了就一句话:

一个事务里的一连串操作,要么全部做完提交,要么中间哪一步翻车了,就全部退回原样,不能停在改了一半的状态。

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 记法各不同

操作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 等机制参与,那属于持久性和崩溃恢复的范畴。

赞(0)
未经允许不得转载:171主机测评 » MySQL 事务原子性:undo log 记什么、存哪、何时清
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址