脏读、不可重复读和幻读是MySQL并发事务中因隔离性不足导致的三类数据不一致问题:脏读读取未提交数据,不可重复读读取已提交的行更新,幻读读取已提交的范围插入/删除。

脏读、不可重复读和幻读,是 MySQL 并发事务中因隔离性不足引发的三类典型数据不一致问题。它们不是 bug,而是不同事务隔离级别下对“读一致性”控制松紧程度的自然体现。理解它们的关键,在于抓住“谁在什么时候读了什么”——尤其是读到了其他事务的哪一阶段(未提交?已提交?刚插入?)的操作结果。
脏读:读到了别人还没定稿的内容
一个事务读取了另一个事务 尚未提交 的修改数据。如果那个事务随后回滚,那读到的数据就从未真实存在过。
比如:事务 A 把用户余额从 100 改成 80,但还没 commit;事务 B 此时查余额,看到的是 80;接着 A 回滚,余额变回 100;而 B 已经按 80 做了后续操作(比如再扣 10),最终余额变成 70 —— 明显错乱。
✅ 触发条件:隔离级别为 READ UNCOMMITTED(MySQL 很少用)。
不可重复读:同一行,两次读,值变了
在同一个事务内,对某一行(或几行)数据进行多次查询,结果不一致。原因在于:其他事务在两次查询之间 修改并提交 了该行数据。
例如:事务 A 第一次查 id=1 的用户名是“张飞”,事务 B 紧接着 update 并 commit 成“张翼德”,A 再查一次就变成“张翼德”。A 感觉自己“读丢了”一次变更。
✅ 解决方式:升级到 READ COMMITTED 可避免脏读,但不可重复读仍可能发生;REPEATABLE READ(MySQL 默认)通过 MVCC 实现快照读,能保证同一事务内多次读同一行结果不变。
幻读:范围查询结果集“多出来”或“少掉”了
事务 A 按条件(如 WHERE status = 1)查询一批记录,得到 N 条;事务 B 在此期间插入或删除了若干条满足该条件的新记录并提交;A 再次执行相同查询,发现变成了 N+M 条(或 N−M 条)—— 数据像“幻影”一样出现或消失。
注意:幻读关注的是 行数变化,不是单行值变化。它常发生在范围查询、分页、insert-before-check 场景中(比如先查“无主键=9 的记录”,再 insert,却报主键冲突)。
✅ MySQL 在 REPEATABLE READ 下通过间隙锁(Gap Lock)+ Next-Key Lock 阻止范围内的插入,基本解决幻读;但严格意义的幻读(如 select for update 后 insert)仍需 SERIALIZABLE 才彻底杜绝。
一句话区分三者核心差异
脏读:读了别人 没提交 的数据;
不可重复读:读了别人 已提交的更新/删除(针对已有行);
幻读:读了别人 已提交的插入/删除(针对新匹配的行)。










