问题: MySQL的四种隔离级别分别解决什么问题?InnoDB默认的RR级别下,Read View的生成规则是什么?它是如何结合MVCC和锁机制彻底解决幻读的?

👇 点击展开/折叠 面试背诵模板(建议先背这个) MySQL的四种隔离级别对应三种并发问题:Read Uncommitted(未提交读)存在脏读;Read Committed(读已提交)解决脏读,但存在不可重复读;Repeatable Read(可重复读,InnoDB默认)解决脏读和不可重复读,并通过MVCC+间隙锁解决幻读;Serializable(串行化)解决所有问题但性能最低。InnoDB的RR下,Read View(一致性视图)在事务第一次执行SELECT快照读时生成,基于当前系统活跃事务ID列表,确保在整个事务期间看到相同的数据快照。针对幻读,InnoDB采用“快照读(MVCC)+ 当前读(Next-Key Lock)”双重策略:普通SELECT通过MVCC读取历史版本,避免幻读;而当前读(如SELECT FOR UPDATE、UPDATE、DELETE)则使用Next-Key Lock(行锁+间隙锁)锁定索引记录间的间隙,阻塞其他事务插入新数据,从而在物理层面杜绝幻读的发生。
---

一、四种隔离级别与并发问题对比

隔离级别 脏读 (Dirty Read) 不可重复读 (Non-Repeatable Read) 幻读 (Phantom Read) 说明
Read Uncommitted(未提交读) ✅ 可能 ✅ 可能 ✅ 可能 存在脏读,几乎不使用
Read Committed(读已提交,RC) ❌ 解决 ✅ 可能 ✅ 可能 Oracle等默认,每次SELECT生成新Read View
Repeatable Read(可重复读,RR) ❌ 解决 ❌ 解决 ✅ InnoDB解决 MySQL默认,事务内第一次SELECT时生成Read View
Serializable(串行化) ❌ 解决 ❌ 解决 ❌ 解决 强制事务串行执行,性能最低

二、Read View(一致性视图)在RR下的生成与可见性规则

生成时机:在RR隔离级别下,Read View在事务内执行第一个快照读(普通SELECT语句)时生成,而非事务启动时。此后整个事务期间复用同一个Read View,直到事务结束,从而保证可重复读。

核心数据结构:

  • m_ids:生成Read View时当前系统中所有活跃事务的ID列表(未提交事务)。
  • min_trx_id:m_ids中的最小值(低水位)。
  • max_trx_id:生成Read View时系统尚未分配的下一个事务ID(高水位)。

可见性判断规则(对某行记录的事务ID trx_id):

  1. 若 trx_id < min_trx_id:该事务在Read View生成前已提交,可见。
  2. 若 trx_id >= max_trx_id:该事务在Read View生成后启动,不可见。
  3. 若 min_trx_id <= trx_id < max_trx_id:若 trx_id 在 m_ids 中(活跃中),则不可见;若不在(已提交),则可见。

注意:RC级别与RR的核心区别在于,RC下每次SELECT都会重新生成Read View,而RR下复用同一个,这就是“可重复读”和“读已提交”的本质差异。

三、MVCC与Next-Key Lock协同彻底解决幻读

幻读指同一事务内,两次执行相同范围的当前读(如 SELECT * FROM t WHERE id > 10 FOR UPDATE)时,结果集行数不一致(其他事务插入了新数据)。

InnoDB在RR下采用分层防御:

① 快照读(MVCC层):普通的 SELECT(无 FOR UPDATE / LOCK IN SHARE MODE)通过复用Read View读取已提交快照,即使其他事务插入了新行,由于新行的事务ID大于Read View的 max_trx_id,对当前事务不可见,因此普通SELECT不会出现幻读。

② 当前读(锁机制层):对于 SELECT ... FOR UPDATE、UPDATE、DELETE 等当前读操作,InnoDB采用 Next-Key Lock = 行锁(Record Lock)+ 间隙锁(Gap Lock)。

  • 行锁:锁定索引记录本身。
  • 间隙锁:锁定索引记录之间的“间隙”(包括边界,防止新数据插入)。

具体作用机制:若执行 SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE,InnoDB不仅锁定 id=10 和 id=20 的行,还会锁定 (10, 20) 之间的所有间隙。此时,其他事务若尝试在该间隙内插入 id=15,会被间隙锁阻塞,直到当前事务释放锁。正是这种物理锁机制,确保了第二次当前读时,范围内不会出现“凭空插入”的新行,从而彻底解决了幻读问题。

四、核心总结与易混淆点澄清

  • RR下普通SELECT无幻读:靠MVCC复用Read View实现。
  • RR下当前读(增删改查FOR UPDATE)无幻读:靠Next-Key Lock在物理上阻塞并发插入实现。
  • 注意区分不可重复读与幻读:不可重复读针对已存在行的字段值变化(靠MVCC/行锁解决),幻读针对结果集行数的变化(靠间隙锁解决)。InnoDB的RR是唯一一个能通过MVCC+间隙锁组合解决幻读的默认隔离级别实现。