脏读指读取到其他事务未提交的数据,发生在read uncommitted级别;不可重复读指同一事务内多次读取同一行数据结果不一致,因其他事务已提交update;幻读指同一查询条件前后返回行数不同,因其他事务insert/delete导致。

脏读:读到了别人还没提交的“临时状态”
脏读发生在一个事务读取了另一个事务 UPDATE 或 DELETE 后、但尚未 COMMIT 的数据。如果那个事务随后 ROLLBACK,你读到的就是根本不存在的值。
典型现象:SELECT 返回了某行数据,但几秒后再次查就没了;或者业务基于这个值做了扣款/发券,结果对方回滚,钱没扣成、券却发出去了。
- 只在
READ UNCOMMITTED隔离级别下出现 -
READ COMMITTED及以上能完全避免 - MySQL 默认不启用该级别,所以实际生产中极少遇到
不可重复读:同一行数据,两次 SELECT 结果不一样
不可重复读关注的是“**单行内容变更**”。同一个事务里,第一次 SELECT id = 1 得到 name = '张三',中间另一个事务执行了 UPDATE ... SET name = '张老三' WHERE id = 1 并 COMMIT,第二次再查就变成 '张老三'。
关键点:不是读到未提交数据,而是读到了已提交但被改过的数据。
- 在
READ COMMITTED级别下会发生(每次SELECT都生成新readview) -
REPEATABLE READ(MySQL 默认)通过 MVCC 快照读避免它 - 注意:
UPDATE/DELETE语句仍会触发当前读,可能看到最新值
幻读:行数对不上,像见了鬼
幻读的本质是“**结果集数量变化**”,不是某一行变了,而是凭空多出来或少掉几行。比如事务 A 执行 SELECT * FROM user WHERE age > 25 查到 3 条,事务 B 插入一条 age = 30 的记录并提交,A 再查就变成 4 条——多出来的那条就是“幻行”。
容易混淆点:幻读 ≠ 不可重复读。前者是范围查询中行数增减,后者是单行值变化。
-
REPEATABLE READ下仍可能发生幻读(尤其INSERT场景) - MySQL InnoDB 用
Next-Key Lock(间隙锁 + 行锁)在可重复读下拦截大部分幻读 - 但纯快照读(如普通
SELECT)无法感知新插入的行;只有当前读(SELECT ... FOR UPDATE、UPDATE、DELETE)才受锁保护
隔离级别怎么选?别只看理论表格
很多资料列个表格说 REPEATABLE READ 解决不可重复读但不解决幻读,这容易误导。实际上:
- MySQL 的
REPEATABLE READ对快照读天然免疫不可重复读和部分幻读 - 但如果你在事务里做
INSERT校验(比如先查是否存在、再插入),又没加SELECT ... FOR UPDATE,就可能因间隙锁缺失导致唯一键冲突或重复插入 -
SERIALIZABLE虽然彻底解决所有问题,但会把并发请求排队执行,吞吐断崖式下跌
真正要注意的,不是“哪个级别理论上支持什么”,而是你代码里每条 SQL 是快照读还是当前读,以及是否覆盖了写操作前的校验路径。











