MySQL间隙锁推理

标签 mysql database-deadlocks

我遇到了僵局,我正试图找出其背后的原因。

问题可以简化为:

表格:

create table testdl (id int auto_increment, c int, primary key (id), key idx_c (c));

隔离级别是可重复读的

(Tx1): 开始;从 testdl 中删除 c = 1000; -- 没有被删除,因为表是空的

(Tx2): 开始;插入 testdl (c) 值 (?);

无论 Tx2 中的值是什么,它都会挂起。所以它基本上意味着当 delete from testdl where c = 1000 找不到匹配项时,Tx1 保持整个范围 (-∞, +∞) 的间隙,对吗?

所以我的问题是:这是设计使然吗?如果是的话,这有什么意义?

更新:

假设我们已经在 testdl 中有了记录:

+----+------+
| id | c    |
+----+------+
|  1 | 1000 |
+----+------+

案例一:

(Tx1): select * from testdl where c = 500 for update; -- c = 500 不存在

(TX2): insert into testdl (c) values (?);

此时可以插入任何>=1000的值,所以Tx1锁定了间隙(-∞, 1000)

同样,是否需要锁定 (-∞, 1000)?这背后的原因是什么?

最佳答案

这和我最近很好奇的问题很相似,所以让我试着解释一下......

Whatever the value in Tx2 is, it hangs. So it basically means that Tx1 holds the gap of the whole range (-∞, +∞), when delete from testdl where c = 1000 fails to find a match, right?.

So my question is: is this by design? What's the point of this if it is?

这是设计使然,间隙锁的要点是防止任何记录插入这些间隙以避免phantom rows .

因此,假设您有一个空表,并且在您执行 delete from testdl where c = 1000; 的事务中。现在,无论在您期望在此查询之后表中没有这样的行之前存在多少这样的行,对吗?因此,如果在那之后您在同一事务中执行 select * from testdl where c = 1000 for update;,您希望它是一个空结果。

但为了确保没有带有 c = 1000 的新行插入到表中,我们需要锁定可以插入此类记录的间隙。在一张空表中只有一个间隙:下确界和上确界伪记录之间的间隙(正如迈克尔指出的那样)。

In this case, any value >= 1000 can be inserted, so Tx1 locks the gap (-∞, 1000)

Again, is locking (-∞, 1000) necessary? What's the reasoning behind this?

我相信上面的解释也应该解释当表中已经有一条记录时你对第二个案例提出的问题。但无论如何我都会尝试解释它。

在您的第一笔交易中,您执行 select * from testdl where c = 500 for update; 现在我们需要确保没有包含 c = 500 的新记录如果我们决定在此交易中再次进行此类查询,则会出现。所以我们需要为它锁定所有必要的间隙。我们还有哪些差距? (-∞, 1000)(1000, +∞),显然 c = 500 的新记录不会被插入第二个gap 但它们会被插入到第一个 gap 中,所以我们必须锁定它。

希望这能解决问题。

关于MySQL间隙锁推理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42545523/

相关文章:

oracle - Oracle 死锁的历史?

nhibernate - 处理 NHibernate 事务错误

mysql - 如何避免具有复合主键的表的索引出现死锁?

sql - 如果以相同的顺序访问对象,如何会发生死锁?

java - MySQL更改为utf8mb4后,Error Unknown character set index for field '224' received from server

c# - DataAdapter.Update() 如何与存储过程一起使用

PHP Socket PDO SQLSTATE[HY000] [2002] 没有这样的文件或目录

mysql - 如何检索两个表中不常见的行

使用多线程代码在一张表上出现 MySQL 死锁

javascript - 使用php和orderby将序列化数组保存到mysql