首页 > 解决方案 > 为什么 MySQL InnoDB 还为更新/删除操作获取 Gap 锁?

问题描述

据我所知,间隙锁用于防止幻读,我发现间隙锁是通过谷歌搜索在大多数文章中锁定读取设置的。

间隙锁是在索引记录之间的间隙上的锁,或在第一条索引记录之前或最后一条索引记录之后的间隙上的锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;防止其他事务将值 15 插入 t.c1 列,无论该列中是否已经存在任何此类值,因为该范围内所有现有值之间的间隙都已锁定。

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-gap-locks

我想这(在锁定读取时设置间隙锁定)就足够了。为什么更新删除还要设置间隙锁。

UPDATE ... WHERE ...在搜索遇到的每条记录上设置一个排他的下一个键锁。但是,对于使用唯一索引锁定行以搜索唯一行的语句,只需要一个索引记录锁。

https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html

另一个问题是如果没有合适的索引可以附加间隙锁会发生什么?是否回退锁定整个表?

这里我们假设使用默认的事务隔离级别Repeatable Read。

标签: mysqldatabaseinnodb

解决方案


这取决于您的 SELECT、UPDATE 或 DELETE 中的条件。他们设置间隙锁以防止其他并发会话将行添加到条件匹配的集合中。

在 InnoDB 中,锁定语句总是锁定最近提交的行版本。所以他们并没有真正遵守 REPEATABLE READ 快照。它们的行为更像 READ-COMMITTED。

因此,如果你做这样的声明:

UPDATE FROM MyTable SET ... WHERE created_at > '2020-03-22';

它必须锁定 created_at 最大值之后的间隙,这将阻止其他会话添加新行。

这是为了模拟REPEATABLE READ,以确保如果再次运行相同的 UPDATE,它将影响相同的行,并且不会意外影响新行。


推荐阅读