mysql事务的原子性由undo log直接实现:每条dml执行时,innodb强制先写入对应undo record(记录旧值及逆操作),再修改数据页;回滚时按链表逐条执行反向操作,确保“全做或全不做”。

undo log 是 MySQL InnoDB 实现原子性的直接载体,不是“辅助手段”,而是事务执行过程中强制写入、与数据修改严格绑定的底层日志结构。
原子性不靠“事务开关”,而靠 undo log 的实时生成与回放
很多人误以为 BEGIN 或 START TRANSACTION 一执行,就“开启原子性保护”。实际上,原子性是在每条 DML(INSERT/UPDATE/DELETE)执行时才真正介入:InnoDB 会立即在当前事务的 undo slot 中写入一条 undo record,记录被修改行的原始版本(包括主键值、列旧值、事务 ID 等)。这个过程和聚簇索引页的修改是同步发生的。
关键点:
-
undo log写入发生在数据页修改之前(即“先写日志,后改数据”),确保即使崩溃,也能用日志还原前像 - 每个事务有独立的
undo segment分配,但undo page可能被多个事务复用(需按 slot 隔离) - 事务未提交时,
undo log必须保留;提交后是否立即清理,取决于是否有其他事务依赖该版本(MVCC 读视图未释放)
rollback 不是“撤销 SQL”,而是按 undo record 逐条反向执行
执行 ROLLBACK 时,InnoDB 并不会重新解析原始 SQL 语句,而是直接遍历本事务产生的 undo log 链,对每条 undo record 执行其内建的逆操作:
-
INSERT对应的 undo record 类型是TRX_UNDO_INSERT_REC,回滚动作就是物理删除该行(或标记为删除) -
UPDATE或DELETE对应的是TRX_UNDO_UPD_EXIST_REC,回滚动作是把旧值拷贝回聚簇索引记录,并恢复roll_ptr和trx_id - 所有这些操作都在内存中完成,不触发二次 SQL 解析或优化器路径
容易被忽略的三个硬约束
原子性看似简单,但在高并发或大事务场景下,undo log 机制会暴露几个真实限制:
-
innodb_undo_tablespaces配置不足会导致 undo 表空间写满,引发ERROR 1760 (HY000): Undo log is full - 长事务会阻塞
undo log清理,导致历史版本堆积,拖慢SELECT性能(尤其在READ COMMITTED下仍需扫描多版本) -
INSERT大量数据时,即使没显式事务,隐式事务也会生成 undo log;若中途断电,重启后 recovery 阶段必须重放或丢弃这些 undo 记录——这就是为什么autocommit=0下手动COMMIT前的崩溃极难恢复
BEGIN,而是每一行变更是否成功落盘了对应的 undo record。一旦这一步失败(比如磁盘满、权限不足、内存分配失败),整个事务连 BEGIN 都算不上有效启动。











