undo log和redo log是innodb实现acid的底层基础设施:undo log记录修改前数据,支撑事务回滚与mvcc快照读;redo log记录物理页修改,基于wal机制保障持久性;二者通过lsn和事务id严格耦合,缺一不可。

Undo Log 和 Redo Log 不是“可有可无的备份日志”,而是 InnoDB 实现事务 ACID 的底层基础设施——没它们,事务提交、回滚、崩溃恢复全都会失效。
Undo Log 保证事务能撤回、MVCC 能并发读
Undo Log 记录的是“修改前的数据”,核心作用有两个:
- 事务回滚时,用它把已写入 Buffer Pool 但未提交的变更逆向还原;
- 其他事务做快照读(
SELECT)时,通过版本链和ROLL_PTR指针找到对应事务可见的旧版本数据,实现 MVCC。
常见错误现象:Lock wait timeout exceeded 或 Transaction is rolled back due to deadlock 后,发现历史数据查不到或不一致,往往不是 SQL 写错了,而是 undo 表空间被过早清理(innodb_max_undo_log_size 设置太小或 innodb_undo_log_truncate 开启过激)。
关键参数注意点:
-
innodb_undo_tablespaces:独立 undo 表空间数量,建议 ≥ 2,避免集中争用; -
innodb_undo_log_truncate:开启后才允许自动截断过期 undo 日志,但需配合innodb_undo_retention(单位秒)控制保留时间; - 长事务会阻止 undo 清理——
show engine innodb status\G中的History list length持续上涨,就是危险信号。
Redo Log 保证事务提交后不丢数据
Redo Log 记录的是“物理页修改操作”,本质是 WAL(Write-Ahead Logging)机制的落地:所有变更必须先记 redo,再改 Buffer Pool,最后才刷脏页到磁盘。
典型场景:执行 UPDATE users SET balance = 100 WHERE id = 1 后 MySQL 突然断电,重启时靠 redo log 把这条修改重放一遍,数据就回来了。
容易踩的坑:
-
innodb_flush_log_at_trx_commit = 1是默认且最安全的设置,设为 0 或 2 会牺牲持久性换取性能,但线上环境几乎不推荐; - redo 文件大小不合理会导致频繁 checkpoint,拖慢写入:
innodb_log_file_size太小(如默认 48M)在高并发写场景下极易触发Log sequence number (LSN)追赶不上刷盘速度,表现为Waiting for log file flush等待; - 调整
innodb_log_file_size必须停库,且不能直接删ib_logfile*文件——否则启动报错InnoDB: Error: log file ib_logfile0 is of different size。
两者协作:一次 UPDATE 背后的日志联动
以 UPDATE t SET v = v + 1 WHERE id = 1 为例,InnoDB 内部实际发生:
- 先写一条 undo log:记录 id=1 原来的
v值(比如 5),存入 undo page; - 再写多条 redo log:包括“修改页 X 的偏移 Y”、“更新 undo page 的链表指针”等物理动作;
- 事务提交时,redo log 刷盘(
fsync),标记 commit;undo log 则进入“准备清理”状态,但要等所有依赖它的事务结束才能真正删除。
这里的关键是:redo 保证“我改过了”,undo 保证“我能退回”——它们不互为镜像,也不共享存储,但通过 LSN 和事务 ID 严格耦合。一旦误删 ib_logfile0 或清空 undo 表空间,数据库可能无法启动,或出现不可逆的数据逻辑错乱。
真正难处理的从来不是“怎么配参数”,而是当 History list length 超过百万、redo log 切换卡顿、或者 crash recovery 卡在 “recovering after a crash” 阶段时,你得立刻判断是 undo 积压、redo 空间不足,还是 buffer pool 与日志系统出现了状态错位——这些细节藏在 SHOW ENGINE INNODB STATUS 和 error log 的字里行间,而不是文档列表里。











