幻读确实存在,指同一事务两次范围查询间因其他事务插入并提交新行而导致结果集新增满足条件的记录;它发生在可重复读级别,尤其在当前读场景下暴露,可通过间隙锁、唯一约束或串行化避免。

幻读确实存在,而且是数据库事务隔离级别中一个真实、可复现的现象,不是理论假设或误读。
什么是幻读
幻读指:在一个事务内,**两次执行相同的范围查询(如 SELECT ... WHERE age > 25)**,第二次查询结果中出现了第一次没有的“新行”,这些行是其他事务在此期间插入并提交的。它和不可重复读不同——不可重复读是同一行数据被修改,而幻读是“凭空多出”了满足条件的新记录。
注意:幻读关注的是 行数变化 和 新符合查询条件的记录出现,不是简单地看到别人插入的任意数据。
幻读在哪些隔离级别下会发生
幻读主要出现在 可重复读(Repeatable Read) 隔离级别下(尤其在 MySQL InnoDB 中需结合具体语句理解)。很多人误以为 RR 能完全避免幻读,其实不然:
- MySQL InnoDB 的 RR 通过 间隙锁(Gap Lock)+ 行锁 在当前读(如 SELECT ... FOR UPDATE、UPDATE、DELETE)中防止幻读;
- 但对 快照读(普通 SELECT),InnoDB 使用 MVCC,只看到事务开始时的已提交快照,所以不会“看到”新插入的行——这看起来没幻读,实则是靠一致性视图“屏蔽”了它;
- 真正暴露幻读的典型场景,是事务 A 先查一次范围,事务 B 插入新行并提交,事务 A 再用 当前读(比如加锁查询或更新)查同一范围——这时就会查到 B 插入的行,构成幻读。
怎么验证幻读(以 MySQL 为例)
你可以用两个会话手动复现:
- 会话 A:开启事务,执行 SELECT * FROM users WHERE age > 20;(得到 3 条);
- 会话 B:插入一条 INSERT INTO users (name, age) VALUES ('Alice', 22); COMMIT;;
- 会话 A:再执行 SELECT * FROM users WHERE age > 20 FOR UPDATE; —— 这次会查到 4 条,新增的 'Alice' 就是幻行。
这个过程清晰展示了幻读的发生机制:不是 MVCC 失效,而是当前读绕过了快照,直接访问最新状态,同时原事务未锁定插入间隙。
如何避免幻读
真正解决幻读,不能只靠隔离级别升级(因为最高级别串行化性能代价大),而要结合机制和设计:
- 使用 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE 对查询范围加锁(InnoDB 会自动加间隙锁),堵住插入漏洞;
- 在应用层加唯一约束或业务校验,避免依赖“查不到就插入”的逻辑(这是幻读高发场景);
- 必要时将隔离级别设为 串行化(Serializable),让数据库自动对范围查询加表级或范围级读锁;
- 理解你的读类型:快照读天然不幻读,但业务若需要强一致性,就得主动用当前读 + 合理锁策略。










