MYSQL+Next-key Lock防止幻读
type
status
date
slug
summary
tags
category
icon
password
本文解释了MYSQL的InnoDB引擎为什么在可重复读(RR)的级别下能防止幻读问题。阅读本文前,建议先了解MYSQL的事务隔离级别和InnoDB对MVCC的实现。
InnoDB存储引擎在 RR 级别下通过 MVCC和 Next-key Lock 来解决幻读问题。- 执行普通
select,此时会以MVCC快照读的方式读取数据 在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成Read View,并使用至事务提交。所以在生成Read View之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”。
- 执行 select...for update/lock in share mode、insert、update、delete 等当前读。
在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!
InnoDB使用 Next-key Lock 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读。
下面举例说明:
现有表person,其中已有3条数据,分别开启两个事务A、B,事务操作都一样,向表中插入一条新数据,id为当前表中MAX(id)+1。B事务先执行插入语句,随后A事务再执行插入语句,会有什么情况发生?
按照
RR隔离级别和MVCC原理,两个事务查询的MAX(id)应该都是从快照查询,值是一样的,也就是说两个事务插入的数据id一致,都是4,且事务A、B是隔离的,它们都认为自己插入的数据id是唯一的,那么两个事务提交后,应该会出现ID重复的错误(产生幻读),那么事实确实如此吗?事务A
事务B
两个事务都在执行插入操作,且使用了
SELECT COALESCE(MAX(id), 0) + 1 来生成新的 ID。在可重复读隔离级别下,这会导致两个事务产生相同的ID,但由于 Next-key Lock 的机制,当 B 事务执行插入时,会锁定读取到的记录和其间隙,防止其他事务在查询范围内插入数据。因此,当 A 事务尝试执行插入时,会被阻塞,直到 B 事务提交。只有在 B 事务提交后,A 事务才能获取到插入的许可,并且执行插入操作。这确保了不会出现相同的 ID,因为 B 事务已经插入了新的记录,而 A 事务在提交时能够看到最新的数据快照。📎 参考文章
- GitTalk