Skip to Content
面试题库Mysql[面试题] InnoDB如何解决脏读、不可重复读和幻读的?

[面试题] InnoDB如何解决脏读、不可重复读和幻读的?

📖 版块 A:知识讲解版 (温故知新)

InnoDB 引擎主要通过 隔离级别MVCC(多版本并发控制)锁机制 这三者协同工作来解决这三个问题。

  1. 脏读 (Dirty Read)

    • 现象:事务A读取了事务B尚未提交的数据。
    • 解决:将隔离级别提高到 读已提交 (Read Committed, RC) 或以上。底层通过 MVCC 在读取时生成的 ReadView 来判断数据版本是否已提交。
  2. 不可重复读 (Non-repeatable Read)

    • 现象:事务A多次读取同一行记录,结果不一致(中间被事务B修改并提交)。
    • 解决:将隔离级别提高到 可重复读 (Repeatable Read, RR)。底层原理是 MVCC 的 ReadView 生成时机:在 RR 级别下,事务仅在第一次执行快照读时生成 ReadView,后续查询复用同一个,从而保证读到的一致。
  3. 幻读 (Phantom Read)

    • 现象:事务A按条件查询范围,事务B插入了新行,导致事务A再次查询时多出了记录。

    • 解决

      • 快照读 (Snapshot Read):通过 MVCC 解决。因为 ReadView 锁定的是事务开始时的快照,新插入的数据版本号不符合可见性规则。
      • 当前读 (Current Read):通过 Next-Key Lock (记录锁+间隙锁) 解决。当执行 SELECT ... FOR UPDATE 等操作时,InnoDB 会锁定记录本身及其周围的“间隙”,防止其他事务在间隙中插入新数据。

🎤 版块 B:面试实战版 (高手逐字稿)

Q:InnoDB如何解决脏读、不可重复读和幻读的?

💬 高手逐字稿

关于 InnoDB 如何处理这三大并发问题,我认为需要从“隔离级别”作为切入点,通过“MVCC”和“锁”这两个底层机制来拆解。

首先,脏读的解决相对简单。InnoDB 只要在“读已提交(RC)”隔离级别下,利用 MVCC 机制,在每次执行查询时都生成一个新的 ReadView,根据 Undo Log 里的事务 ID 判断,只读取那些已经提交的版本,就能完全规避脏读。

其次是不可重复读。这是在“可重复读(RR)”隔离级别下解决的。InnoDB 的核心逻辑是改变了 ReadView 的生成时机:在 RR 下,一个事务只在第一次执行快照读时生成 ReadView,整个事务过程中都复用这一个视图。这样即使别的事务修改并提交了数据,当前事务看到的依然是最初那个版本,从而实现了可重复读。

最关键的是幻读,这是 InnoDB 的一个亮点。在标准 SQL 定义中,RR 级别是不要求解决幻读的,但 InnoDB 在 RR 级别下基本解决了幻读:

  1. 如果是快照读(普通的 SELECT),它继续依靠 MVCC。因为新插入的“幻影”记录的事务 ID 肯定不在当前事务的可见范围内,所以查不到。
  2. 如果是当前读(比如 SELECT ... FOR UPDATEUPDATE 语句),InnoDB 会动用 Next-Key Lock。它不仅锁住索引记录本身,还会加上 Gap Lock(间隙锁)。这就像是在记录之间的缝隙里撒了钉子,其他事务想在这个范围内插入新数据会被阻塞,从根本上杜绝了幻读的产生。

这里有一个容易被忽略的细节(高手自问自答): 虽然 InnoDB 在 RR 下做了很多努力,但在一种极端情况下依然会出现幻读。比如事务 A 先执行快照读发现没有某条记录,接着事务 B 插入并提交了该记录,此时如果事务 A 突然执行一条 UPDATE 语句去更新这条原本“不存在”的记录,由于 UPDATE 是当前读,它会把这条记录的版本号更新为自己的事务 ID,导致之后的快照读能看到这条记录。这算是 InnoDB 在性能和一致性权衡下留下的一个边缘场景。

总的来说,InnoDB 是通过 MVCC 解决快照读下的问题,通过 Next-Key Lock 解决当前读下的幻读问题,从而在 RR 级别下就实现了非常高的数据一致性。

最后更新于:
⏱️

登录后开始阅读

当前 MVP 版本只开放邮箱密码登录注册。完成登录后即可进入面试题内容。

还没有账号?先去注册