🗄️ MySQL 事务核心实现原理

ACID · 隔离级别 · 锁机制 · MVCC · 日志系统 · 两阶段提交

一、事务核心全景流程

事务从 BEGIN 开始,经历锁管理 → 日志写入 → 隔离性保障 → 最终提交或回滚

🚀 BEGIN

事务开启

🎯

📋 分配事务ID

生成唯一XID

🔒 获取隔离级别

决定并发控制策略

⚙️

📝 执行 SQL

读写数据页

🔐 写 Undo Log

记录旧值

📝 写 Redo Log

记录新值

🛡️

⚡ MVCC 检查

可见性判断

✅ COMMIT / ❌ ROLLBACK

两阶段提交 · 释放锁 · 清理Undo

💾 持久化完成

数据写入磁盘

二、ACID 四大特性实现机制

🅰️ Atomicity(原子性)

  • 事务是最小执行单元,不可再分
  • 所有操作要么全部成功,要么全部失败回滚
  • 实现:Undo Log
  • 记录每个修改的反操作,失败时逆向恢复
  • 即使系统崩溃,也能回滚到事务开始前状态

🅲 Consistency(一致性)

  • 事务执行前后,数据库状态保持正确
  • 所有约束(主键、外键、唯一键)不被破坏
  • 实现:锁 + 约束检查
  • 数据库完整性规则在提交前强制校验
  • 通过原子性和隔离性共同保证

🅸 Isolation(隔离性)

  • 并发事务之间互不干扰
  • 每个事务感觉自己是独立执行的
  • 实现:锁机制 + MVCC
  • 通过不同隔离级别控制并发程度
  • 在性能与准确性之间做权衡

🅳 Durability(持久性)

  • 事务提交后,数据必须持久保存
  • 即使系统崩溃也不能丢失
  • 实现:Redo Log + 刷盘策略
  • 事务提交时,日志必须先刷到磁盘
  • 崩溃后从日志恢复未刷盘的数据
💡 核心理解:原子性靠 Undo Log,隔离性靠锁+MVCC,持久性靠 Redo Log,一致性是最终目标,三者共同保证。

三、四大隔离级别与问题对照

隔离级别 脏读 不可重复读 幻读 锁策略
READ UNCOMMITTED ✅ 可能 ✅ 可能 ✅ 可能 无锁,性能最高,风险最大
READ COMMITTED ❌ 不可能 ✅ 可能 ✅ 可能 行级锁,Oracle默认级别
REPEATABLE READ ❌ 不可能 ❌ 不可能 ❌ 可能(普通查询)
❌ 不可能(加锁读)
Gap锁,MySQL默认级别
SERIALIZABLE ❌ 不可能 ❌ 不可能 ❌ 不可能 表级锁,性能最低,串行执行

🔍 三种并发问题详解

🩸 脏读 (Dirty Read)

事务A读取到事务B未提交的修改,事务B回滚后数据不存在

📊 不可重复读 (Non-repeatable)

事务A在同一次读取中,两次得到不同的值(因为事务B修改并提交了)

👻 幻读 (Phantom Read)

事务A按条件查询,事务B插入了新行并提交,导致事务A第二次查询多了行

MySQL 中隔离级别与 MVCC + 锁的关系

REPEATABLE READ(默认)

快照读用 MVCC
当前读用行锁+Gap锁

READ COMMITTED

每次 SELECT 生成新快照
只读取已提交的事务

SERIALIZABLE

所有读加锁
强制串行

四、锁机制全图

🔐 锁的分类

按维度划分

📖 共享锁 S读锁,可并发
✏️ 排他锁 X写锁,独占
🔲 表锁锁定整张表
📊 行锁锁定单行
⬛ Gap锁锁定区间
🔢 记录锁锁定索引
📋 意向锁表级标识

🔄 锁的兼容性矩阵

SX
S 兼容✅ 兼容❌ 冲突
X 冲突❌ 冲突❌ 冲突

🎯 当前读(加锁)

SELECT ... FOR UPDATE

INSERT / UPDATE / DELETE

📷 快照读(无锁)

普通 SELECT(不带 FOR UPDATE)

利用 MVCC 读取历史版本

五、MVCC 多版本并发控制

🎯 MVCC 核心思想

每行数据存储多个版本,事务读取时根据 Read View(读取视图) 判断哪个版本可见,从而实现读不加锁、读写不冲突

🔢 trx_id事务版本号
🕐 undo_ptr回滚指针
📋 Read View快照可见性
📜 roll_ptr版本链指针

📜 聚簇索引行结构中的 MVCC 字段

trx_id

修改事务ID

roll_ptr

指向Undo链

db_roll_id

回滚段ID

🔗 版本链示意(多个事务修改同一行)

最新版本
v3 (trx=30)
v2 (trx=20)
新值B
v1 (trx=10)
原始值A
NULL
链尾

🎯 Read View 判断规则

遍历版本链,找到第一个 trx_id < min_trx_id 或已提交的版本

RC 模式下 Read View 生成时机

每个 SELECT 语句开始时,都会重新生成一个 Read View。所以能看到其他事务已提交的修改。

RR 模式下 Read View 生成时机

事务中第一次 SELECT 时生成 Read View,之后复用同一个快照。整个事务期间读到的数据一致,不会看到新插入的行(解决幻读)。

六、日志系统:Redo vs Undo

🔁Redo Log(重做日志)

  • 目的:保证持久性(D)
  • 记录:物理页的变更("把哪个页的哪个偏移改成什么")
  • 写入时机:数据页修改时实时写入(Write-Ahead Logging)
  • 用途:崩溃恢复,redo 未刷盘的已提交事务
  • 存储位置:ib_logfile0 / ib_logfile1(循环写)
  • 格式:Mini-Transaction (MTR),原子性写多个页面
-- 提交事务时必须保证: 1. Redo Log 刷盘(持久化); 2. 数据页刷盘(可选); 3. 返回客户端成功;

↩️Undo Log(回滚日志)

  • 目的:保证原子性(A)+ 隔离性(I)
  • 记录:逻辑反操作("UPDATE 把 id=1 的 name 改成 B,UNDO 要改回 A")
  • 写入时机:数据修改前写入,用于回滚未提交事务
  • 用途:回滚 + MVCC 历史版本读取
  • 存储位置:系统表空间(ibdata1)的 Undo Segment
  • 清理时机:没有事务引用时由 Purge 线程清理
-- UPDATE 的 Undo 示例: UPDATE t SET name = 'B' WHERE id=1; -- Undo 记录:把 name='B' 改回 'A'

修改 Buffer Pool

数据页在内存中

写入 Redo Log

记录变更(MTR)

写 Undo Log

记录逆向操作

COMMIT 提交

刷 Redo → 返回成功

后台刷盘

脏页异步落盘

七、两阶段提交(2PC)

⚡ 为什么需要两阶段提交?在分布式/多存储引擎架构下(如 InnoDB + binlog),需要保证数据页更新和 binlog 的一致性。两阶段提交确保任何一个环节失败都能回滚,最终要么全部成功,要么全部失败。
阶段一:Prepare 阶段二:Commit

📝 执行 SQL,写入 InnoDB Redo Log

标记为 prepared 状态

📋 写入 binlog(Server层)

binlog 刷盘完成

🔖 标记事务为 commit

Redo Log 写 commit 标记

💾 事务完成

数据持久化成功

Prepare 成功,但 binlog 写失败 → 回滚 InnoDB

binlog 写成功,但 Redo commit 失败 → 重试 commit(MySQL 内部处理)

⚠️ 若 binlog 写入后崩溃,MySQL 认为事务已提交(binlog 已持久化)

八、崩溃恢复流程

步骤 1:定位检查点(Checkpoint)

MySQL 重启后读取 ib_logfile 中的 LSN(Log Sequence Number)检查点,确定已刷盘的最大位置

步骤 2:重做已提交事务(Redo)

扫描 Redo Log,将 checkpoint 之后所有已提交但未刷盘的事务重新执行,使数据页恢复到最新状态

步骤 3:回滚未提交事务(Undo)

对于 prepare 但未 commit 的事务,根据 Undo Log 执行回滚,释放所有锁

步骤 4:清理

Purge 线程清理不再需要的 Undo 页面,释放空间

💡 为什么先 Redo 再 Undo?Redo 是让已提交的事务生效(向前恢复),Undo 是撤销未提交事务(向后恢复)。必须保证已提交的修改被恢复后,才能安全地回滚未提交的修改。

九、事务完整执行流程(综合全景)

🖥️ 客户端发起事务

BEGIN / AUTO-COMMIT

📝 SQL 执行

解析 → 优化 → 执行计划

🔒 获取锁

行锁 / Gap锁 / IX锁

📝 写 Undo Log

记录修改前的镜像

🔄 修改 Buffer Pool

更新数据页(内存)

📝 写 Redo Log

记录修改内容(MTR)

COMMIT

Redo 刷盘 → 释放锁

⚙️ InnoDB 服务端处理

🔢 分配事务ID

trx_id 唯一标识

📋 创建 Read View

(快照读时)

⚡ MVCC 检查

判断版本可见性

🔄 数据页变更

标记为脏页

📝 Undo 链追加

形成版本链

🔖 写 binlog

(Server层两阶段)

💾 异步刷脏页

Checkpoint 机制

十、核心知识点串联总结

🧠 MySQL 事务知识全景图

🚀 BEGIN → COMMIT / ROLLBACK

事务开始与结束

🅰️ Atomicity

靠 Undo Log 回滚

🅲 Consistency

靠锁+约束保证

🅸 Isolation

靠锁+MVCC

🅳 Durability

靠 Redo Log 刷盘

📝 Undo Log

原子性+MVCC历史版本

📝 Redo Log

持久性+崩溃恢复

🔐 锁机制

并发控制+隔离级别

⚡ MVCC

读不阻塞写+版本链

表锁 行锁 Gap锁
Record Lock Next-Key Lock
S 锁 X 锁 IS IX
Read View trx_id roll_ptr
binlog Checkpoint
两阶段提交

RU 脏读

RC 不可重复读

RR 幻读(MySQL)

SERIAL 串行

🔗 核心关联链:

事务 = BEGIN + Undo(原子性) + Redo(持久性) + 隔离级别(并发控制)→ COMMIT

Read View隔离级别 决定生成时机
版本链Undo Log + roll_ptr 构成
两阶段提交 保证 Redo + binlog 一致性

MySQL 事务核心原理 · 流程图总结 · 覆盖 ACID / 隔离级别 / 锁机制 / MVCC / 日志系统 / 两阶段提交 / 崩溃恢复