MySQL事务核心机制详解
一、事务的四大特性(ACID)
| 特性 | 英文 | 说明 |
|---|---|---|
| 原子性 | Atomicity | 事务中的操作要么全部成功,要么全部失败(通过undo log实现) |
| 一致性 | Consistency | 事务执行前后,数据必须保持逻辑一致(由其他三个特性共同保障) |
| 隔离性 | Isolation | 多个事务并发执行时,相互不干扰(通过锁 + MVCC实现) |
| 持久性 | Durability | 事务提交后,数据永久保存(通过redo log实现) |
简单记忆:原一隔持(原子、一致、隔离、持久)
二、并发事务会引发的问题
| 问题 | 说明 | 举例 |
|---|---|---|
| 脏读 | 读到其他事务未提交的数据 | A转账给B,A未提交,B查账发现钱已到 |
| 不可重复读 | 同一事务内,两次读取同一行数据结果不一致 | 事务T1两次查询余额,期间T2修改了余额 |
| 幻读 | 同一事务内,两次查询记录数不一致 | 事务T1查用户列表共5条,期间T2插入1条,T1再查变6条 |
注意:不可重复读侧重"修改/删除",幻读侧重"新增"
三、隔离级别及解决问题
MySQL InnoDB支持四种隔离级别(由低到高):
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED(读未提交) | ✔️ | ✔️ | ✔️ |
| READ COMMITTED(读已提交,RC) | ❌ | ✔️ | ✔️ |
| REPEATABLE READ(可重复读,RR) | ❌ | ❌ | ✔️* |
| SERIALIZABLE(串行化) | ❌ | ❌ | ❌ |
*注:MySQL InnoDB的RR级别通过间隙锁解决了幻读问题
设置隔离级别
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置会话级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置全局级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
四、MVCC(多版本并发控制)
1. 为什么需要MVCC?
-
提高并发性能:读不加锁,写加锁,读写不互斥
-
解决读写冲突 ,实现非阻塞读
2. MVCC依赖的三个隐藏字段
| 字段 | 含义 |
|---|---|
| DB_TRX_ID | 最近修改该行记录的事务ID |
| DB_ROLL_PTR | 指向undo log中该行历史版本的指针 |
| DB_ROW_ID | 行ID(无主键时自动生成) |
3. Read View(读视图)
在RC 和RR隔离级别下,查询会生成Read View,用于判断哪个版本可见。
Read View包含:
-
m_ids:活跃事务ID列表 -
min_trx_id:最小活跃事务ID -
max_trx_id:下一个待分配的事务ID -
creator_trx_id:当前事务ID
4. 可见性判断规则
根据DB_TRX_ID与Read View对比:
-
若
DB_TRX_ID < min_trx_id:可见(已提交) -
若
DB_TRX_ID >= max_trx_id:不可见(未来事务) -
若
min_trx_id <= DB_TRX_ID < max_trx_id:-
在
m_ids中 → 不可见(活跃事务) -
不在
m_ids中 → 可见(已提交)
-
5. RC与RR的Read View生成时机差异
| 隔离级别 | Read View生成时机 | 效果 |
|---|---|---|
| RC | 每条SELECT语句都重新生成 | 可以读到其他事务已提交的最新修改(不可重复读) |
| RR | 第一条SELECT时生成,整个事务复用 | 保证多次查询结果一致(可重复读) |
五、幻读及解决方案(重点)
1. 什么是幻读?
sql
-- 事务T1
SELECT * FROM user WHERE age = 20; -- 查到2条
-- 事务T2插入一条age=20的记录并提交
SELECT * FROM user WHERE age = 20; -- 查到3条 → 幻读
2. MySQL如何解决幻读?
在RR级别 下,InnoDB使用间隙锁解决幻读。
间隙锁(Gap Lock)
-
锁定一个范围(不存在的记录位置)
-
防止其他事务在锁定范围内插入新记录
举例说明
sql
-- 假设user表id有1, 2, 5, 8
SELECT * FROM user WHERE id BETWEEN 3 AND 7 FOR UPDATE;
-- 会锁定间隙:(2,5] 和 (5,8),不允许插入id=4或6等记录
解决幻读完整流程
-
事务T1执行
SELECT ... WHERE条件查询 -
InnoDB不仅对现有记录加行锁,还对查询范围加间隙锁
-
事务T2尝试在间隙范围内
INSERT→ 阻塞等待 -
T1提交释放锁,T2才能继续 → 幻读被阻止
3. 需要注意
-
快照读(普通SELECT,不加锁):依赖MVCC,不会加间隙锁,但RR下通过Read View也能避免幻读
-
当前读(SELECT ... FOR UPDATE / LOCK IN SHARE MODE):依赖间隙锁防止幻读
快照读:MVCC + Read View(历史版本)
当前读:行锁 + 间隙锁(最新数据)
六、总结对比表
| 概念 | 作用 | 实现依赖 |
|---|---|---|
| 原子性 | 事务回滚 | undo log |
| 持久性 | 数据持久化 | redo log |
| 隔离性 | 并发控制 | 锁 + MVCC |
| 一致性 | 逻辑完整性 | 原子+持久+隔离 |
| MVCC | 读写不互斥 | 隐藏字段 + undo log + Read View |
| 幻读解决(RR) | 防止新增幻影行 | 间隙锁(当前读)/ MVCC(快照读) |
七、常见面试问题
Q1:MySQL默认隔离级别是什么?
A:REPEATABLE READ(可重复读)
Q2:RC和RR哪个性能更好?
A:RC锁范围更小,并发能力更高,但会出现不可重复读;RR保证可重复读但间隙锁可能降低并发。
Q3:MVCC完全解决了幻读吗?
A:快照读下完全解决;当前读下通过间隙锁也解决了。所以InnoDB的RR级别可以说彻底解决了幻读。
Q4:串行化级别如何工作?
A:对所有读隐式加读锁,读写完全互斥,并发最低。