脏读、幻读、不可重复读是什么?
脏读、幻读、不可重复读是什么?
zhangchangsheng面试考察点
- 基础掌握度:面试官不仅仅是想知道三个概念的定义,更是想知道你是否理解它们产生的根本原因 —— 并发事务之间的数据干扰,以及它们之间的本质区别。
- 隔离级别理解:考察你是否清楚 MySQL 的四种事务隔离级别,以及每种隔离级别能解决哪些并发问题。
- 实践应用能力:能否根据业务场景选择合适的隔离级别,是否了解 MVCC(多版本并发控制)如何解决这些问题。
核心答案
脏读、不可重复读、幻读是 数据库并发事务 中三种典型的数据不一致问题:
| 问题 | 定义 | 关注点 | 严重程度 |
|---|---|---|---|
| 脏读 | 读到其他事务未提交的数据 | 数据被回滚 | ⭐⭐⭐ 最严重 |
| 不可重复读 | 同一事务内,两次读取同一行数据结果不同 | 数据被修改/删除 | ⭐⭐ 中等 |
| 幻读 | 同一事务内,两次查询结果行数不同 | 数据被新增 | ⭐ 较轻 |
一句话总结:脏读是读了未提交的数据;不可重复读是同一条数据变了;幻读是结果集行数变了。
深度解析
一、三种问题详解
| 时间线 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; |
|
| T2 | BEGIN; |
|
| T3 | UPDATE users SET age=25 WHERE id=1;(未提交) |
|
| T4 | SELECT age FROM users WHERE id=1; → 读到 age=25(未提交!) |
|
| T5 | ROLLBACK;(回滚了!) |
|
| T6 | 我读到的数据是脏数据… |
问题:事务 A 读到了事务 B 未提交的数据,事务 B 回滚后,事务 A 拿到的数据就不合法了。
上图展示了脏读的发生过程:
- 时间点 T3:事务 B 将
id=1的age从 20 改为 25,但尚未提交 - 时间点 T4:事务 A 读取该行数据,得到
age=25 - 时间点 T5:事务 B 回滚了,数据恢复为 20
- 问题:事务 A 读到的
age=25是 “脏数据”,因为事务 B 根本没提交
脏读危害:基于未提交数据做业务判断,可能导致严重的逻辑错误。
| 时间线 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; |
|
| T2 | SELECT age FROM users WHERE id=1; → 读到 age=20 |
|
| T3 | BEGIN; |
|
| T4 | UPDATE users SET age=25 WHERE id=1; |
|
| T5 | COMMIT; |
|
| T6 | SELECT age FROM users WHERE id=1; → 读到 age=25(变了!) |
|
| T7 | 同一事务两次读取,结果不一致! |
问题:事务 A 内两次读取同一行数据结果不同,因为事务 B 在中间修改并提交了这条数据。
上图展示了不可重复读的发生过程:
- 时间点 T2:事务 A 第一次读取,
age=20 - 时间点 T4~T5:事务 B 修改了这条数据并提交
- 时间点 T6:事务 A 再次读取同一行,
age=25 - 问题:同一事务内,两次读取同一数据,结果不一致
与脏读的区别:不可重复读读到的是已提交的数据,不是脏数据。
| 时间线 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; |
|
| T2 | SELECT * FROM users WHERE age > 20; → 返回 2 条记录 |
|
| T3 | BEGIN; |
|
| T4 | INSERT INTO users(age) VALUES(30); |
|
| T5 | COMMIT; |
|
| T6 | SELECT * FROM users WHERE age > 20; → 返回 3 条记录! |
|
| T7 | 同一条件查询,结果行数变了! |
问题:事务 A 内两次相同条件查询结果行数不同,因为事务 B 在中间新增了一条符合条件的数据。
上图展示了幻读的发生过程:
时间点 T2:事务 A 查询
age > 20的记录,返回 2 条时间点 T4~T5:事务 B 新增了一条
age=30的记录并提交时间点 T6:事务 A 再次用相同条件查询,返回 3 条
问题:同一事务内,两次相同查询,结果行数不同,仿佛出现了 “幻影”
与不可重复读的区别:不可重复读强调 同一条数据被修改(UPDATE/DELETE)
幻读强调 结果集行数变化(INSERT/DELETE)
二、四种隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED(读未提交) | 可能发生 | 可能发生 | 可能发生 |
| READ COMMITTED(读已提交) | 已解决 | 可能发生 | 可能发生 |
| REPEATABLE READ(可重复读) | 已解决 | 已解决 | 已解决 |
| SERIALIZABLE(串行化) | 已解决 | 已解决 | 已解决 |
注:MySQL InnoDB 默认 REPEATABLE READ,通过 MVCC + Next-Key Lock 在很大程度上解决了幻读问题。
隔离级别详解:
- READ UNCOMMITTED(读未提交)
- 最低隔离级别
- 可能发生脏读、不可重复读、幻读
- 几乎不使用
- READ COMMITTED(读已提交,RC)
- 只能读取已提交的数据
- 解决了脏读,但可能发生不可重复读、幻读
- Oracle、SQL Server 默认级别
- REPEATABLE READ(可重复读,RR)
- MySQL InnoDB 默认隔离级别
- 保证同一事务内多次读取同一数据结果一致
- 解决了脏读、不可重复读
- InnoDB 通过 MVCC + Next-Key Lock 也解决了幻读
- SERIALIZABLE(串行化)
- 最高隔离级别
- 强制事务串行执行
- 解决所有问题,但性能最差
三、MySQL 如何解决这些问题
| 机制 | 要点 |
|---|---|
| MVCC(多版本并发控制) | 每行数据保存 DB_TRX_ID(最后修改的事务 ID);读操作根据 Read View 判断可见性;读不阻塞写,写不阻塞读;RC 级别每次 SELECT 生成新的 Read View,RR 级别只在第一次 SELECT 生成 Read View |
| 锁机制 | 共享锁(S 锁)用于读操作,排他锁(X 锁)用于写操作;行锁锁定单行记录;间隙锁(Gap Lock)锁定一个范围;Next-Key Lock = 行锁 + 间隙锁 |
| RR 级别解决幻读 | 快照读(普通 SELECT)通过 MVCC 解决;当前读(SELECT … FOR UPDATE)通过 Next-Key Lock 锁住范围,防止新增 |
MVCC 解决不可重复读:
-- RR 级别下,MVCC 保证可重复读 |
Next-Key Lock 解决幻读:
-- RR 级别下,当前读使用 Next-Key Lock |
四、对比总结
| 对比项 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读取的数据 | 未提交数据 | 已提交数据 | 已提交数据 |
| 关注点 | 内容是否有效 | 内容是否一致 | 行数是否变化 |
| 涉及操作 | UPDATE / DELETE | UPDATE / DELETE | INSERT / DELETE |
| 严重程度 | 最严重 | 中等 | 较轻 |
| 解决方案 | 读已提交(RC) | 可重复读(RR) | 串行化,或 RR + Next-Key Lock |
面试高频追问
- MySQL 默认隔离级别是什么?如何解决幻读?
- 默认是
REPEATABLE READ(可重复读) - 通过 MVCC 解决快照读的幻读
- 通过 Next-Key Lock(行锁 + 间隙锁)解决当前读的幻读
- 默认是
- RC 和 RR 的区别?
RC:每次SELECT生成新的Read View,能看到其他事务已提交的修改RR:第一次SELECT生成Read View,后续复用,保证可重复读
- 什么是 MVCC?
- 多版本并发控制,每行数据保存版本链(通过
undo log) - 读操作根据
Read View判断哪个版本对自己可见 - 实现 “读不阻塞写,写不阻塞读”
- 多版本并发控制,每行数据保存版本链(通过
- 什么时候用 SERIALIZABLE?
- 需要严格数据一致性的场景(如金融交易)
- 一般不推荐使用,性能太差
常见面试变体
- “MySQL 有哪些事务隔离级别?”
- “RR 级别能完全解决幻读吗?”
- “MVCC 是怎么实现的?”
- “什么是 Next-Key Lock?”
记忆口诀
脏读未提交,数据不可信; 不可重复读,同条数据变; 幻读多或少,结果行数变。 RC 解脏读,RR 解后俩,串行全解决。
总结
脏读是读到未提交的数据(最严重);不可重复读是同一事务内同一行数据变了(UPDATE/DELETE);幻读是同一事务内查询结果行数变了(INSERT/DELETE)。MySQL 默认使用 REPEATABLE READ 隔离级别,通过 MVCC 解决不可重复读,通过 MVCC + Next-Key Lock 解决幻读。
声明:本文转载自小哈的博客 《脏读、幻读、不可重复读是什么?》,版权归原文作者所有。转载请保留本声明与原文链接。
