前提:事务 ACID,隔离级别就是用来解决这三类问题,MySQL InnoDB 默认隔离级别:
REPEATABLE‑READ(可重复读)
先准备一张测试表 user
表格
| id | name | money |
|---|---|---|
| 1 | A | 1000 |
1、脏读 Dirty Read
一个事务读到了另一个事务未提交的数据,对方最后回滚了,读到的数据就是 “脏数据”。
时序:
- 事务 B 更新 A 的 money = 0,没有 commit
- 事务 A 查询,读到 money=0(读到未提交)
- 事务 B rollback 回滚,数据变回 1000
- 事务 A 手里拿着一个已经不存在的数据 0 → 脏读
- 隔离级别:读未提交 (Read Uncommitted) 才会出现脏读
- MySQL InnoDB 不会出现脏读,最低级别就规避了脏读。
2、不可重复读 Non‑Repeatable Read
同一个事务内,同一条数据,前后两次读取,中间被别的事务修改并提交,两次结果不一样。
时序:
- 事务 A:查询 id=1 money=1000
- 事务 B:更新 id=1 money=500,
commit提交 - 事务 A:再次查询同一条 id=1,money 变成 500
同一个事务,两次读同一行,结果变了 → 不可重复读
重点:针对同一行记录,被别的事务修改 / 删除。
隔离级别:读已提交 (Read Committed) 会出现不可重复读;可重复读解决不可重复读。
⚠注意:不要和幻读搞混。
3、幻读 Phantom Read
同一个事务,前后两次范围查询,别的事务插入 / 删除了新行,导致前后返回行数不一样,像产生了幻影。
时序:
- 事务 A:
select * from user where money>0,查到 2 条记录 - 事务 B:
insert插入一条新记录,commit - 事务 A:再次执行完全一样的 where 范围查询,返回 3 条,多出来一行幻影数据
关键点:不是修改老行,是新增 / 删除行,影响结果集行数。
MySQL InnoDB 在
REPEATABLE‑READ(可重复读)通过 MVCC + Next‑Key Lock(临键锁) 解决了幻读。其他数据库比如 PostgreSQL,可重复读级别依然存在幻读。
4、可重复读 REPEATABLE‑READ(MySQL InnoDB 默认)
同一个事务内,多次读取,看到的始终是事务启动那一刻的快照,其他事务提交的修改,对当前事务不可见。
依靠 MVCC 多版本并发控制,读快照,不加锁。
特点:
- ✅ 杜绝脏读
- ✅ 杜绝不可重复读
- ✅ InnoDB 下杜绝幻读(临键锁)
- 事务开启之后,不管外面别的事务怎么改、新增,我多次查询结果不变。
注意:快照读和当前读区分
普通 select → 快照读 (MVCC,可重复读)
select ... for update→ 当前读,拿最新真实数据,会加锁。
四个隔离级别从低到高
表格
| 隔离级别 | 脏读 | 不可重复读 | 幻读 (InnoDB) |
|---|---|---|---|
| Read Uncommitted 读未提交 | 会发生 | 会发生 | 会发生 |
| Read Committed 读已提交 | ❌不会 | 会发生 | 会发生 |
| Repeatable‑Read 可重复读 (MySQL 默认) | ❌不会 | ❌不会 | ❌不会 (innodb 临键锁) |
| Serializable 串行化 | ❌不会 | ❌不会 | ❌不会 |
隔离级别越高,锁越多,并发性能越低。
面试一句话速记
- 脏读:读到别人没提交的数据,对方回滚,拿到脏数据。
- 不可重复读:同一行,中间被别人修改提交,事务内两次读结果不一样。
- 幻读:范围查询,别人插入 / 删除,返回行数发生变化,多出幻影行。
- 可重复读:MySQL InnoDB 默认级别,MVCC 快照,同一个事务多次读取结果保持不变,解决脏读、不可重复读,依靠临键锁解决幻读。
面试高频追问
Q:RC(读已提交)和 RR(可重复读)区别?
- RC:每次 select 都拿最新快照;会出现不可重复读;锁粒度相对小,并发好。
- RR:事务启动生成快照,整个事务沿用这份快照;解决不可重复读,InnoDB 解决幻读。
Q:MVCC 是什么?
多版本并发控制,保存数据历史版本,读不加锁,读写不冲突,实现可重复读。
Q:幻读和不可重复读核心区别?
不可重复读:修改 / 删除已有行;
幻读:插入新记录,影响结果集行数。




