连载中 7/22

事务与崩溃恢复:ACID 背后的 redo 和 undo

2026-05-05 · 7350 阅读 · 0 评论 · 0 赞

一次灵异扣款

聊完优化器,篇篇都是查询。老王上周遇到的事更吓人:机房闪断重启后,用户反馈「余额扣了,订单没了」。查了一圈不是 Bug——两个更新在两个事务里,扣款事务提交了,订单事务还没来得及提交就赶上断电。这个问题暴露的是团队对事务的理解停留在「begin 和 commit 之间自动全对」。ACID 不是口号,是两本日志撑出来的:redo log 和 undo log。

先厘清:ACID 各归谁管

特性靠什么实现
原子性 Atomicityundo log:做一半崩溃时,把改过的全部撤销
持久性 Durabilityredo log:提交了就不许丢,崩溃后能重放恢复
隔离性 Isolation锁 + MVCC(第 8、9 篇)
一致性 Consistency由前三者共同保证的目的,不是独立手段

redo log:先把账记下来

一个事务改了十条数据,分散在十个不同的数据页里,每个页都在 B+ 树的不同位置——直接把十个页刷盘就是十次随机写,太慢。InnoDB 的解法是 WAL(Write-Ahead Logging,先写日志):事务提交时只把「哪个页做了什么物理修改」顺序追加进 redo log(顺序写,极快),数据页先留在内存的 Buffer Pool 里慢慢刷。

崩溃了怎么办?重启后把 redo log 重放一遍,没来得及落盘的修改全部补上——顺序写的日志,换随机写的数据页,持久性就是这么买到的。redo log 是固定大小的文件组,循环写:write pos 一直往前追,checkpoint 之后的区域是待写入空间,追上 checkpoint 就得停下来先刷脏页推进 checkpoint——这也是为什么 redo 设太小的实例高峰期写入会周期性抖动。

控制「提交时日志刷多严」的参数,是事务安全和性能的三角:

innodb_flush_log_at_trx_commit = 1   # 每次提交都 fsync 落盘,最安全(默认)
# = 2 提交写到操作系统缓存,每秒 fsync:进程崩溃不丢,断电最多丢 1 秒
# = 0 每秒才写才刷:MySQL 进程崩了也可能丢 1 秒

和 binlog 的 sync_binlog 组合出来的「双 1 配置」,第 10 篇细说。

undo log:后悔药

undo log 记录的是逻辑反操作:insert 记下主键好回滚时删除,update 记下旧值好回滚时还原。它有两个职责:一是事务回滚时把数据还原;二是给 MVCC 提供旧版本——其他事务读这行时,如果看到的是历史版本,版本就顺着 undo log 的链找(伏笔,下一篇的主角)。

提交后 undo 不会立刻删,要等没有事务再需要这些历史版本时,由 purge 线程清理。由此得出一条重要的工程纪律:别开长事务——一个几小时不提交的事务,会让它之后的所有 undo 都清不掉,历史版本堆积、表空间膨胀、查询变慢,连坐全库。

崩溃恢复的完整剧本

现在能回答「断电后 MySQL 重启发生了什么」:

  • 前滚(redo):重放 redo log,把已提交、但数据页还没刷到磁盘的修改补齐。
  • 回滚(undo):找出崩溃时还提交中的事务,用 undo log 把它们改过的数据全部撤销。

一句话:redo 负责让已提交的必达,undo 负责让未提交的作废。两本日志一配合,原子性和持久性同时成立。回头看老王的灵异扣款:它不是事务失效,是两个业务动作被错误地拆成了两个事务——该在一个事务里的操作,永远放进一个事务。

redo 和 undo 的地基打好,下一楼就是隔离级别与 MVCC:不同事务同时读写一行时,各自看到的到底是什么版本?ReadView 是怎么画出来的?下一篇揭晓。

咖啡凉了,记得趁热喝。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#MySQL#事务#redo log#undo log#ACID

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞