MySQL 事务核心机制

用费曼学习法,把面试官最想问的 5 个概念一次讲透

费曼学习法四步: ① 选概念:ACID、隔离级别、MVCC、RR 默认、2PC
② 讲给小孩听:用最简单的语言 + 生活类比
③ 找漏洞:哪里卡壳 → 那里回头补
④ 简化比喻:把抽象概念具象成日常场景

1三个日志的分工

undo / redo / binlog —— 三兄弟各司其职,别搞混

一句话定义

MySQL 里跑事务,靠三种日志配合:undo log 记"改之前的值"、redo log 记"改了之后要重放什么"、binlog 记"这个操作是谁发起的"。

图书馆比喻

把数据库想成一个图书馆:undo log 是"修改记录本"——每次改书之前抄一份原文;redo log 是"施工日志"——记着哪本书哪页要改成什么;binlog 是"借阅流水"——记着每次操作是谁做的,供另一个图书馆(从库)照抄。

三兄弟的关键差异

undo log
引擎层 · 逻辑
旧值 · 回滚 · MVCC
redo log
引擎层 · 物理
页修改 · 崩溃恢复
binlog
Server 层 · 逻辑
SQL/Row · 复制

为什么需要两个 redo 类日志?

redo log 是"引擎内部",用来自己恢复;binlog 是"给外部看",用来复制给从库。职责完全不同,缺一不可——这也正是 2PC 要协调的原因(后面第 6 节讲)。

自检:binlog 能保证数据不丢吗?
不能。持久性靠 redo log(WAL 机制),binlog 只负责复制和灾备。只写 binlog 不刷 redo,MySQL 崩溃后内存中的页修改会丢。

2ACID 是如何实现的

四个字母,每个字母背后都有具体机制

四性对应

字母含义核心机制
A原子性 Atomicityundo log(回滚)
C一致性 Consistency约束 + 业务代码 + 前三性
I隔离性 Isolation锁(行锁/间隙锁) + MVCC
D持久性 Durabilityredo log + 2PC

点外卖比喻

原子性:一份订单要么完全送到,要么完全不送,不会送到一半。
一致性:送之前先检查地址对不对,钱扣了菜没到就是 BUG,业务逻辑要保证。
隔离性:你下单不影响别人下单,各点各的。
持久性:订单一旦确认,就算系统崩了,重启后订单还在。

每个字母的关键机制

A · undo log:记录旧值。事务失败按 undo log 反向重放恢复原状。同时参与 MVCC。

C · 业务保证:数据库只保证 A/I/D 三个,一致性靠约束(NOT NULL、UNIQUE、外键)+ 业务代码逻辑正确。

I · 锁 + MVCC:写操作用悲观锁(行锁/间隙锁),读操作用 MVCC 快照读。

D · redo + 2PC:redo log 先刷盘(WAL),配合 2PC 保证 redo 与 binlog 一致。

最容易踩的坑

面试高频错点:把 undo log 说成"重做日志"。undo = 撤销、redo = 重做,方向相反,别搞反!
一句话记忆:A 靠 undo、D 靠 redo + 2PC、I 靠锁 + MVCC、C 靠大家一起保证。

3四种事务隔离级别

从 RU 到 Serializable,每升一级多挡一种"读异常"

三种读异常

异常现象从哪级解决
脏读读到别人未提交的数据RC 起
不可重复读同一事务两次读,结果不一致RR 起
幻读范围查询看到"新插入的行"RR 大部分解决

餐厅比喻

RU:你刚看菜单,服务员还在改,你看到的可能不是最终价。
RC:每次问价都拿到最新价,但你问了两次可能不一样。
RR:进入餐厅那一刻菜单就"拍照"了,整个用餐期间看到的价格都是快照的。
Serializable:每次问价都要排队,前面的人问完你才问,价格永远一致但很慢。

RC 和 RR 的核心差异

维度RCRR
ReadView 生成每次 SELECT 新建事务第一次 SELECT 生成一次
锁机制只有行锁行锁 + 间隙锁(next-key)
不可重复读存在解决
幻读存在部分缓解
性能更高略低

记住这张图

RU 读未提交 RC 读已提交 · Oracle默认 RR 可重复读 · MySQL默认 Serializable 串行化 解决的问题: RC → 脏读 RR → 不可重复读 + 大部分幻读 SER → 所有读异常

4MVCC 是怎么工作的

多版本并发控制 —— 读不阻塞写、写不阻塞读的魔法

三件套

① 行隐藏字段:每行数据偷偷带 DB_TRX_ID(谁改的)、DB_ROLL_PTR(上一版在哪)、DB_DELETE_MARK(是否删除)。
② undo log 版本链:每次 UPDATE 生成新版本,旧版本挂到链上。
③ ReadView:读数据时创建快照视图,用事务 ID 判断哪一版可见。

维基百科比喻

每次有人修改维基百科词条,都不会覆盖原版,而是新增一个版本。你打开词条时,看到的版本取决于你打开的那一刻"哪些修改已经保存"。MVCC 就是让数据库每行都变成一个"维基百科"——多个版本共存,读者只看到自己该看的那一版。

ReadView 四个字段 + 可见性判定

ReadView = {
  m_ids:           [在活动时创建 ReadView 的活动事务 ID],
  min_trx_id:      m_ids 中最小的,
  max_trx_id:      下一个要分配的事务 ID (max+1),
  creator_trx_id:  创建者自己
}

可见性判定(拿一行数据的 trx_id 对比):

条件结果原因
trx_id == creator可见自己改的
trx_id < min_trx_id可见早于所有活动事务,已提交
trx_id ≥ max_trx_id不可见ReadView 之后才有的
min ≤ trx_id < max,在 m_ids不可见在活动列表,可能未提交
min ≤ trx_id < max,不在 m_ids可见不在活动列表说明已提交

不可见时沿 DB_ROLL_PTR 找上一版继续判断。

RC vs RR 的唯一区别

RC:每次 SELECT 都新建 ReadView → 每次看到最新已提交
RR:事务第一次 SELECT 生成一次,之后复用 → 整个事务看到一致快照
Interactive Demo · 试试调整 ReadView 参数,看可见性如何变化
自检:SELECT FOR UPDATE 会走 MVCC 吗?
不走。MVCC 只服务快照读(普通 SELECT)。当前读(SELECT FOR UPDATE / UPDATE / DELETE)直接看最新已提交版本,并加锁保证一致性。

5为什么 MySQL 默认 RR

这是一次"一致性与性能的折中",不是"性能优先"

核心逻辑

四种级别里,RR 是一致性和性能的平衡点:既不太松(会踩坑),也不太严(性能不炸)。默认的哲学是"让用户默认安全,有需求再调"。

买保险比喻

保险公司有几种保单,默认给你的不是最便宜也不是最贵的,而是"最常见的中等方案"——大部分场景够用,特殊情况再调整。RR 就是这个中等方案。

RR 保持性能的关键:MVCC

如果隔离级别越高越慢,那 RR 就该被淘汰。但 InnoDB 的 MVCC 让 RR 的快照读不加锁,读性能不损失。所以 RR 是"用一致性换锁,不用锁换性能"——这才是 MySQL 敢默认 RR 的底气。

那互联网公司为什么切 RC?

RR 的痛点

间隙锁死锁多 · 锁范围大 · 锁等待长

RC 的优势

只有行锁 · 冲突面小 · 并发高

什么时候坚持 RR

金融账务 · 强一致业务 · 订单核心

面试雷区:说"RR 是为了效率"是错的!RR 是一致性优先的折中,RC 才更省锁。答反直接扣分。

6两阶段提交 2PC

MySQL 内部 2PC 和分布式 2PC 是两回事

先分清两种 2PC

MySQL 内部 2PC

协调 redo log 和 binlog,保证两份日志数据一致

分布式事务 2PC

协调 多个服务/数据库,让它们一起提交或回滚

拍合影比喻

拍照前先喊"3、2、1、看镜头",让大家准备好姿势(Prepare);然后一起按快门(Commit)。如果拍照前有人没准备好喊"重来",就整体重来(Abort)。MySQL 内部 2PC 就是这个思路——redo 和 binlog 都是"参与者",InnoDB 是"协调者"。

MySQL 2PC 三阶段流程

1

Phase 1 · Prepare

InnoDB 把 redo log 写入 prepare 状态,但不 commit。此时事务"已准备就绪但没提交"。

2

Phase 2 · 写 binlog

MySQL Server 把 binlog 写入并刷盘。binlog 是"提交成功的标志"。

3

Phase 3 · Commit

binlog 写完后,InnoDB 把 redo log 从 prepare 改为 commit。事务完成。

崩溃恢复判断表

redo 状态binlog 状态处理
prepare未写回滚
prepare已写提交
commit已写已完成,重放即可
核心不变量:binlog 是"提交成功的标志",binlog 没写就不 commit。

分布式 2PC 为什么业务不常用?

因为它是阻塞式协议:协调者挂了参与者一直等、网络抖动就卡住、Prepare 到 Commit 之间有数据不一致窗口。所以现代系统改用 TCC(Try-Confirm-Cancel)、Saga(长事务拆步骤)、Seata AT(无侵入自动补偿)等柔性方案。

面试雷区:不要说"MySQL 2PC 是先写日志再批量提交到磁盘"——那是组提交,跟 2PC 是两回事。2PC 解决一致性,组提交解决性能。

速查表 · 面试前 3 分钟看完

三个日志

ACID 一句话

隔离级别

MVCC 三件套

2PC 三阶段

⚠️ 三个高频错误