文章目录
〇、事务基础:什么是 ACID?
1. 定义
事务(Transaction) 是数据库操作的最小逻辑单元。它包含了一组不可分割的数据库操作序列,这些操作要么全部成功执行,要么全部不执行。
2. 核心特性 (ACID)
事务的四个基本要素,是数据库从一种一致性状态转换到另一种一致性状态的保障:
| 特性 | 英文 | 描述 | 实现关键 |
|---|---|---|---|
| 原子性 | Atomicity | 誓言:“要么全做,要么全不做”。操作不可分割。 | Undo Log |
| 一致性 | Consistency | 目标:事务前后,数据保持逻辑上的完整和一致。 | 由A、I、D共同保障 |
| 隔离性 | Isolation | 防护:并发事务之间互不干扰,仿佛在独立执行。 | 锁 + MVCC |
| 持久性 | Durability | 承诺:一旦提交,数据永久保存,断电也不丢失。 | Redo Log |
一、MySQL 的“三大日志”
MySQL 的强大依赖于三种核心日志:Redo Log(重做日志)、Undo Log(回滚日志) 和 Bin Log(归档日志)。理解它们是掌握 ACID 实现的关键。
1. Redo Log (重做日志) —— 保障持久性
Redo Log 是 InnoDB 存储引擎独有的日志,遵循 WAL (Write-Ahead Logging) 技术,即“先写日志,再写磁盘”。
- 作用:记录“在某个数据页上做了什么修改”。用于数据库崩溃后的故障恢复。
- 物理结构:循环写入的固定大小文件。
- 写入流程:
- 事务修改内存中的数据。
- 生成日志写入
Redo Log Buffer。 - 根据策略(如
innodb_flush_log_at_trx_commit)刷入磁盘上的 Redo Log 文件。 - 崩溃恢复:重启时读取 Redo Log,重放未刷脏页的操作。
2. Undo Log (回滚日志) —— 保障原子性 & MVCC
Undo Log 记录了数据修改的逆向逻辑。
- 作用:
- 事务回滚:执行 rollback 时,将数据恢复到修改前。
- MVCC(多版本并发控制):构建数据的历史版本链,实现快照读。
- 逻辑结构:
- 执行
INSERT-> 记录DELETE - 执行
UPDATE-> 记录UPDATE回旧值
- 执行
- 生命周期:事务开始前产生,事务提交后并不会立刻删除(因为 MVCC 可能还需要它),由 Purge 线程清理。
Q: 同一个事务内一条记录被多次修改,Undo Log 怎么记?
A: 每次修改前,都会将当前版本的数据写入 Undo Log。这会形成一条 Undo Log 版本链,链首是最新的旧数据,链尾是最老的旧数据。
3. Bin Log (归档日志) —— 全局备份
Bin Log 是 MySQL Server 层(所有引擎共享)的日志,记录了所有对数据库结构和数据的修改。
- 特性:
- 追加写:文件写满后切换新文件,不会覆盖旧记录。
- 逻辑日志:记录的是 SQL 语句的原始逻辑(Statement 格式)或数据行的变更(Row 格式)。
- 核心作用:
- 主从复制:Slave 获取 Master 的 Bin Log 进行同步。
- 数据恢复:使用
mysqlbinlog工具恢复误删数据。
📊 总结:三大日志对比
| 维度 | Redo Log | Undo Log | Bin Log |
|---|---|---|---|
| 归属 | InnoDB 引擎层 | InnoDB 引擎层 | MySQL Server 层 |
| 侧重 | 物理日志(页修改) | 逻辑日志(逆操作) | 逻辑日志(SQL/行变更) |
| 主要职责 | 崩溃恢复(持久性) | 回滚 & MVCC(原子性) | 主从复制 & 归档 |
| 写入方式 | 循环覆盖写 | 顺序写 | 追加写 |
二、ACID 的底层实现图解
- 原子性 (Atomicity) ➔ Undo Log
- 事务执行出错或手动 Rollback 时,利用 Undo Log 逆向还原数据。
- 持久性 (Durability) ➔ Redo Log
- 利用 WAL 机制,保证即使内存数据未刷盘,断电后也能通过 Redo Log 恢复。
- 隔离性 (Isolation) ➔ 锁 + MVCC
- 下文详细展开。
- 一致性 (Consistency) ➔ 最终目标
- 通过上述三个特性,加上代码层的逻辑判断(如转账余额校验),共同保证一致性。
三、深入剖析:隔离性 (Isolation)
MySQL 通过 锁 和 MVCC (多版本并发控制) 的配合,在性能与数据安全性之间寻找平衡。
1. 并发事务的“三害”
如果不加控制,并发事务会引发以下问题:
- 脏读 (Dirty Read):读到了别人未提交的数据。
- 不可重复读 (Non-Repeatable Read):同一事务内,两次读取同一行数据,结果不一样(被别人 UPDATE/DELETE 并提交了)。
- 幻读 (Phantom Read):同一事务内,两次查询范围数据,结果条数不一样(被别人 INSERT 并提交了)。
2. 事务隔离级别与解决方案
MySQL 默认隔离级别为 可重复读 (Repeatable Read, RR)。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现机制 |
|---|---|---|---|---|
| 读未提交 (RU) | ❌ | ❌ | ❌ | 读不加锁,写加行锁 |
| 读已提交 (RC) | ✅ | ❌ | ❌ | MVCC (每次查询生成新 ReadView) |
| 可重复读 (RR) | ✅ | ✅ | ✅(绝大部分) | MVCC (第一次查询生成 ReadView) + Next-Key Lock |
| 串行化 (Serializable) | ✅ | ✅ | ✅ | 读写都加锁,串行执行 |
纠正误区:
- RC (读已提交) 解决了脏读,但无法解决不可重复读。
- RR (可重复读) 解决了脏读和不可重复读,并基于 MVCC 和间隙锁解决了大部分幻读问题。
3. 锁机制:从粗粒度到细粒度
- 全局锁:
Flush tables with read lock。全库只读,通常用于全库逻辑备份。 - 表级锁:锁整张表,开销小但并发度低。
- 行级锁 (InnoDB 核心):
- Record Lock (记录锁):锁住索引记录本身。
- Gap Lock (间隙锁):锁住索引之间的间隙,防止插入(解决幻读的关键)。
- Next-Key Lock (临键锁):
Record Lock + Gap Lock。锁住记录本身以及前面的间隙,前开后闭区间。
4. MVCC 与快照读
MVCC 是通过 Undo Log 版本链和 Read View(一致性视图)实现的。
- 快照读 (Snapshot Read):普通的
SELECT语句。不加锁,读取记录的可见版本。 - 当前读 (Current Read):
SELECT ... FOR UPDATE、UPDATE、DELETE。读取最新版本,并加锁。
四、常见疑难 Q&A
Q1: RR 级别下真的完全没有幻读吗?
绝大多数情况下没有,但有特殊情况。
- 常规情况:MVCC 保证了快照读看不到新插入的数据;Next-Key Lock 保证了当前读(写操作)无法插入新数据。
- 特殊情况:
- 事务 A 先进行快照读(无数据)。
- 事务 B 插入一条数据并提交。
- 事务 A 对这条“看不见”的数据执行了
UPDATE操作。 - 结果:事务 A 再次查询时,就能看到这条数据了(因为 UPDATE 变成了当前读,更新了版本号,使其对当前事务可见)。这就是幻读的“漏网之鱼”。
Q2: 什么是死锁?如何解决?
- 成因:基于两阶段锁协议(锁在需要时申请,事务结束时释放)。如果两个事务互相持有对方需要的锁并等待,就会形成死锁回路。
- 案例:
- 事务 A: 锁住 Row 1 -> 等待 Row 2
- 事务 B: 锁住 Row 2 -> 等待 Row 1
- 解决方案:
- 超时机制:
innodb_lock_wait_timeout,超时自动回滚。 - 死锁检测(默认开启):
innodb_deadlock_detect = on。MySQL 主动检测死锁图,回滚成本较小的那个事务。
- 超时机制:
参考资料
- MySQL 官方文档 - InnoDB Locking and Transaction Model
- 高性能 MySQL (第4版)
本文详细介绍了MySQL事务的概念,包括ACID特性、事务的实现方式以及日志系统如RedoLog、Undolog和Binlog的作用。文章还讨论了事务的原子性、持久性和隔离性的实现,特别提到了在可重复读隔离级别下如何解决幻读问题,并简述了死锁的产生及解决方案。
:深入理解 MySQL 事务,从日志原理到 ACID 实现&spm=1001.2101.3001.5002&articleId=129478756&d=1&t=3&u=52d8e40651a545348cf1d0d5a7888957)
1万+

被折叠的 条评论
为什么被折叠?



