Skip to main content

幻读与脏读的区别

幻读脏读是数据库事务并发控制中的两种不同现象,它们的核心区别在于:脏读读的是“未提交”的修改数据,而幻读读的是“新增或删除”的行记录

为了帮你清晰区分,我们从三个维度来剖析:

1. 核心定义(本质区别)

  • 脏读(Dirty Read):一个事务读取了另一个事务尚未提交(随后可能回滚)的数据。如果读取后该事务回滚,读到的数据就是无效的“脏数据”。
  • 幻读(Phantom Read):一个事务在前后两次执行同一个查询时,返回的结果集行数不同(变多或变少)。这是因为另一个事务在这期间**插入(INSERT)或删除(DELETE)**了符合查询条件的新数据。

2. 影响的数据范围(操作类型)

  • 脏读:针对现有数据行修改(UPDATE)。重点在于数据的“值”是否被确认。
  • 幻读:针对数据集合新增(INSERT)或删除(DELETE)。重点在于数据“条数”或“集合边界”发生了变化。

3. 对应的隔离级别(SQL标准)

  • 脏读:发生在 READ UNCOMMITTED(读未提交)级别。任何高于该级别的隔离级别(如 READ COMMITTED)都能防止脏读。
  • 幻读:发生在 REPEATABLE READ(可重复读)级别(在 MySQL/InnoDB 中除外,其 RR 可防止幻读,但标准 SQL 中允许幻读)。只有在 SERIALIZABLE(串行化)级别下才能完全防止幻读。

举例说明(时间线对比)

假设有一个 账户表,初始数据为:用户A:余额 100元

场景一:脏读(读到了未提交的修改)

时间事务1(转账)事务2(查账)
T1开启事务,将A余额改为 0元(未提交)
T2开启事务,查询A余额,读到 0元(脏读)
T3发生异常,执行 ROLLBACK(回滚),余额恢复为100元
T4基于读到的0元继续业务逻辑(数据错误)

后果:事务2读到了根本不存在的数据(0元),因为事务1最终回滚了。


场景二:幻读(读到了新增/删除的行)

时间事务1(统计)事务2(插入)
T1开启事务,统计年龄>18岁的用户,结果为 10人
T2开启事务,插入1个新的成年用户,并 COMMIT(提交)
T3再次执行同一条统计SQL,结果变为 11人(幻读)
T4事务1发现前后数据条数不一致,产生困惑

后果:事务1在同一事务内,两次读取的结果集集合发生了变化,仿佛出现了“幻觉”。


一句话总结口诀

  • 脏读:读到了别人还没定下来的修改(会滚回去的)。
  • 幻读:读到了别人新加进来的记录(或者少了记录),导致数量对不上了。

特别提示(针对 MySQL):如果你使用 MySQL 的 InnoDB 引擎,在 REPEATABLE READ 默认隔离级别下,InnoDB 通过 MVCC(多版本并发控制)和间隙锁(Gap Lock)解决了幻读问题,但标准 SQL 定义中幻读是允许在该级别下发生的,这一点在面试或学习理论时需要注意区分。