当前位置: 首页 > 知识库问答 >
问题:

为什么mysql间隙锁中非唯一索引的next-key lock不退化成行锁?

姬旭
2023-07-10

CREATE TABLE t (

id int(11) NOT NULL AUTO_INCREMENT,

k int(11) DEFAULT NULL,

PRIMARY KEY (Id),

KEY k (k)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表里三行数据(0,0)(5,5)(10,10)

我们知道,如果唯一索引等值查询会退化成行锁,比如:
select * from t where id=5 for update 只会锁住id=5这一行。
但是
select * from t where k=5 for update,就要锁住k in (0,10)整个区间。

难道是后一种情况下插入类似(3,3)这样的行会产生幻读或者导致binlog日志和数据不一致的问题,能举一个例子吗。我想了半天没想出这样的例子。
既然能在唯一索引上做退化成行锁的优化,为什么就不能应用在非唯一索引上呢?是工程上的问题吗,还是说其实非唯一索引也能应用这样的优化,只是mysql没做?

共有2个答案

巩选
2023-07-10

楼主说得没错,就是退化成行级别锁可能导致幻读问题。

如果将 非唯一 索引上存在重复值时使用行级别锁来代替 Next-Key Lock ,则可能允许其他事务插入一个与当前正在进行查询或扫描操作相关联但具有相同键值(但不同主键)的新记录。这就导致了幻读现象,破坏了事务的隔离性。

我举个例子,假设有如下表 employees

ID Name Salary
1 张三 5000
2 李四 6000
3 王五 7000

事务A:

BEGIN;
SELECT * FROM employees WHERE Salary > 5500 FOR UPDATE;

事务B:

BEGIN;
INSERT INTO employees (Name, Salary) VALUES ('马六', 6500);
COMMIT;

此时,事务A将会锁住满足条件 Salary > 5500 的记录,并且在查询结果上设置 Next-Key Locks 。这意味着除了已经存在的记录(ID=2 和 ID=3)之外,也会锁住间隙 (5000,6000] 和 (6000,7000]。

如果非唯一索引的Next-Key Lock可以退化成行级别锁,那么事务B将能够成功插入一条工资为6500的记录,导致幻读 问题。

所以事务B必须 等待 事务A释放对间隙 (6000,7000] 的锁定才能继续执行(所以 Next-Key Locks 不能是行级别锁)。这确保了数据的完整性和一致性,并避免了幻读现象。

胥宏义
2023-07-10

next-key lock锁定的区间是左开右闭。当你只有(0, 0)、(5, 5)、(10, 10)三条数据时,字段K,其区间范围为(-∞, 0]、(0, 5]、(5, 10]、(10, ∞],因此,当K=5的情况时,锁定了,两个区间,加起来就是(0,10]区间了。

next-key lock的锁定区间,其主要目的是为了解决幻读。我理解如果是在RR级别下,有next-key lock解决幻读,这样的例子应该是举不出来。可以在RC级别试试,这时候没解决幻读,应该不存在next-key lock了。

在唯一索引上,退化成行锁,是因为可以找到唯一的一条记录,就可以使用Record lock(单个行记录的锁)。注意:如果查询不存在的唯一记录也是会导致gap lock。例如:select * from t where id=6 for update。而非唯一索引,本身就可能在数据库中存在多行数据,因此,我理解不能退化成Record lock是正常的。

 类似资料:
  • 问题内容: 就性能而言,MySQL唯一索引和非唯一索引有什么区别? 假设我要在2列的组合上创建索引,并且该组合是唯一的,但是我创建了一个非唯一的索引。这会对MySQL使用的性能或内存产生重大影响吗? 同样的问题, 主 键和 唯一 索引之间有区别吗? 问题答案: UNIQUE和PRIMARY KEY是 约束 ,而不是索引。尽管大多数数据库通过使用索引来实现这些约束。除了索引之外,约束的额外开销也微不

  • 本文向大家介绍MySQL中InnoDB的间隙锁问题,包括了MySQL中InnoDB的间隙锁问题的使用技巧和注意事项,需要的朋友参考一下  在为一个客户排除死锁问题时我遇到了一个有趣的包括InnoDB间隙锁的情形。对于一个WHERE子句不匹配任何行的非插入的写操作中,我预期事务应该不会有锁,但我错了。让我们看一下这张表及示例UPDATE。   InnoDB状态显示这个UPDATE在主索引记录上持有了

  • 本文向大家介绍唯一索引比普通索引快吗, 为什么?相关面试题,主要包含被问及唯一索引比普通索引快吗, 为什么?时的应答技巧和注意事项,需要的朋友参考一下 唯一索引不一定比普通索引快, 还可能慢. 查询时, 在未使用limit 1的情况下, 在匹配到一条数据后, 唯一索引即返回, 普通索引会继续匹配下一条数据, 发现不匹配后返回. 如此看来唯一索引少了一次匹配, 但实际上这个消耗微乎其微. 更新时,

  • 本文向大家介绍通过唯一索引S锁与X锁来了解MySQL死锁套路,包括了通过唯一索引S锁与X锁来了解MySQL死锁套路的使用技巧和注意事项,需要的朋友参考一下 在初学者从源码理解MySQL死锁问题中介绍了使用调试 MySQL  源码的方式来查看死锁的过程,这篇文章来讲讲一个常见的案例。 这次我们讲一段唯一索引 S 锁与 X 锁的爱恨情仇 我们来看一个简化过的例子 我们用之前介绍过的源码分析方式,先来看

  • 本文向大家介绍react中遍历时为什么不用索引作为唯一的key值?相关面试题,主要包含被问及react中遍历时为什么不用索引作为唯一的key值?时的应答技巧和注意事项,需要的朋友参考一下 key作为组件的唯一标志,在数组中删除时,虽然对应的组件删除了,但是对应的索引值会被自动替代.比如3个组件,删除第2个,真正想要的是[1,3].但展示的可能是[1,2]

  • 问题内容: 不知道在PostgreSQL 9.3+中是否可行,但是我想在非唯一列上创建唯一索引。对于像这样的表: 我想仅能[快速]查询不同的日子。我知道我可以用来帮助执行不同的搜索,但是如果不同值的数量大大少于索引覆盖的行数,这似乎会增加额外的开销。就我而言,大约30天中有1天与众不同。 我是创建关系表以仅跟踪唯一条目的唯一选择吗?思维: 并在每次插入数据时使用触发器来更新它。 问题答案: 索引只