SQL数据库事务隔离级别_幻读与不可重复读

15次阅读

不可重复读指同一事务内多次读取同一行数据结果不一致,因其他事务修改或删除该行;幻读指同一事务内按相同条件多次查询结果集行数变化,因其他事务插入或删除符合该条件的新行。

SQL数据库事务隔离级别_幻读与不可重复读

事务隔离级别决定了多个并发事务之间如何相互影响,其中“幻读”和“不可重复读”是两种典型的现象,它们都源于事务对数据的读取结果在执行过程中发生意外变化,但触发条件和本质不同。

什么是不可重复读

在一个事务内,多次读取同一行数据,结果不一致——比如第一次读到 age=25,第二次读变成 age=28。这通常是因为其他事务在这期间对该行执行了 UPDATE 或 delete 并已提交。

  • 核心特征:针对同一行数据,值被修改或删除
  • 发生场景:事务 A 读取某行 → 事务 B 修改/删除该行并提交 → 事务 A 再次读取,发现数据变了
  • 可被 REPEATABLE READ 隔离级别解决(通过行锁或快照机制)

什么是幻读

在一个事务内,按相同条件多次查询,返回的记录数不一致——比如第一次查到 3 条 name LIKE ‘张%’ 的记录,第二次却查到 4 条。这是因为其他事务插入(INSERT)了符合该条件的新行并已提交。

  • 核心特征:针对同一查询条件,结果集行数变化(新增或消失)
  • 发生场景:事务 A 查询一批数据 → 事务 B 插入/删除符合该条件的新行并提交 → 事务 A 再次查询,结果集“凭空多出”或“少掉”记录
  • 仅靠行锁无法完全避免,需更高一级的控制(如间隙锁、临键锁,或升级到 SERIALIZABLE

各隔离级别对两者的影响

不同数据库实现略有差异,以 mysql InnoDB 为例:

  • READ UNCOMMITTED:允许脏读、不可重复读、幻读
  • READ COMMITTED:避免脏读,但不可重复读和幻读仍可能发生(每次 select 都读最新已提交快照)
  • REPEATABLE READ(InnoDB 默认):避免脏读和不可重复读;幻读在普通 SELECT 中被 MVCC 隐藏,但在 当前读(如 SELECT … for UPDATE、UPDATE WHERE)下仍可能因新插入而出现
  • SERIALIZABLE:通过强制串行化执行,彻底避免三者;但性能开销最大

如何应对幻读

实际开发中,不必盲目升到 SERIALIZABLE。更实用的方式包括:

  • 使用 SELECT … FOR UPDATESELECT … LOCK IN SHARE MODE 显式加锁,配合唯一约束或业务校验
  • 在应用层增加幂等性设计,例如插入前先查重,或用唯一索引拦截重复
  • 合理使用 INSERT … ON DUPLICATE KEY UPDATEMERGE 类语句,减少竞态窗口
  • 理解数据库的锁策略:InnoDB 的临键锁(Next-Key Lock)能同时锁住记录和间隙,是 RR 级别下防止幻读的关键机制

区分幻读与不可重复读的关键,在于关注变化的是“单行值”还是“结果集范围”。理解这一点,才能选对隔离级别和锁策略,既保障一致性,又不牺牲必要性能。

text=ZqhQzanResources