Undo Log核心作用是保障事务原子性和MVCC:UPDATE执行前即写入完整行旧值+事务元数据构成逻辑前镜像,支持回滚与版本链构建,缺失则导致脏读、死锁、快照失效及崩溃恢复失败。

因为Update必须支持事务回滚和MVCC——不先写Undo Log,就无法保证原子性,也无法让其他事务读到一致性快照。
UPDATE执行前就分配并写入Undo Log
很多人以为Undo Log是事务提交或回滚时才生成的,其实不是。UPDATE语句刚进入执行器、还没真正改数据页之前,InnoDB就已为该事务分配Undo Log segment,并把修改前的完整行记录(逻辑前镜像)写入内存中的undo buffer。这个动作不可跳过,也不可延迟。
- 即使
autocommit=1,单条UPDATE也隐式开启事务,同样触发Undo Log写入 - Undo Log写入会立刻触发对应
redo log刷盘——因为Undo Log自身也要持久化,不能丢 - 如果此时MySQL崩溃,重启后可通过Undo Log回滚未提交事务,保障原子性
Undo Log里到底记了什么(以UPDATE为例)
不是只记被改的字段,而是整行旧值+事务元数据:id=1, name='zhangsan', age=25, trx_id=100, roll_pointer=0xabc。这些信息共同构成“逻辑前镜像”,用于后续回滚或MVCC可见性判断。
-
trx_id:标识这版旧值由哪个事务产生,供ReadView比对 -
roll_pointer:指向更早版本的Undo Log,串成版本链 - INSERT/DELETE对应的Undo Log结构不同:INSERT只记主键,DELETE记全行
- SELECT不产生Undo Log——只有DML才触发
不先写Undo Log会导致哪些具体问题
最直接后果是事务失去“后悔能力”,且并发读会看到脏数据或不一致状态。典型现象包括:
-
ERROR 1205 (40001): Deadlock found when trying to get lock:Undo Log链堆积+清理滞后,锁等待升级为死锁 - 长事务未提交 → Purge线程无法清理对应Undo Log →
ibdata1或undo_001持续膨胀 → 最终触发ERROR 1114 (HY000): The table is full - RR隔离级别下,老事务执行
SELECT读不到旧值:因为缺少Undo Log版本链,MVCC失效 - 崩溃恢复后,未提交事务的数据无法回滚,破坏ACID中的Atomicity
真正容易被忽略的点在于:Undo Log的生命周期不由事务提交时间决定,而取决于所有依赖它的ReadView是否全部退出——哪怕一个只读事务还在运行,它引用的Undo Log就无法被Purge线程回收。


















