MySQL间隙锁定推理

Dea*_*ter 6 mysql database-deadlocks

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

问题可以简化为:

表:

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

隔离级别可重复读取

(Tx1): begin; delete from testdl where c = 1000; -- nothing is deleted coz the table is empty

(Tx2): begin; insert into testdl (c) values (?);

无论Tx2中的值是多少,它都会挂起。因此,从根本上说,当delete from testdl where c = 1000找不到匹配项时,Tx1保持整个范围的间隙(-∞,+∞),对吗?

所以我的问题是:这是设计使然吗?这是什么意思呢?

更新:

假设我们已经有以下记录testdl

+----+------+
| id | c    |
+----+------+
|  1 | 1000 |
+----+------+
Run Code Online (Sandbox Code Playgroud)

情况1:

(Tx1): select * from testdl where c = 500 for update; -- c = 500 not exists

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

在这种情况下,可以插入> = 1000的任何值,因此Tx1锁定间隙(-∞,1000)

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

GPr*_*ost 5

这与我最近好奇的事情类似,所以让我尝试解释一下......

\n\n
\n

无论 Tx2 中的值是什么,它都会挂起。所以这基本上意味着 Tx1 保留整个范围的间隙(-\xe2\x88\x9e,+\xe2\x88\x9e),当从 c = 1000 的 testdl 中删除时找不到匹配项,对吗?

\n\n

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

\n
\n\n

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

\n\n

因此,想象一下您有一个空表,并且在您执行的事务中delete from testdl where c = 1000;。现在,无论在您之前存在多少这样的行,您都希望在此查询之后您的表中没有这样的行,对吧?所以,如果之后你这样做select * from testdl where c = 1000 for update;在同一笔交易中执行操作,您预计它会是一个空结果。

\n\n

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

\n\n
\n

在这种情况下,任何>= 1000的值都可以插入,因此Tx1锁定间隙(-\xe2\x88\x9e, 1000)

\n\n

再说一次,锁定 (-\xe2\x88\x9e, 1000) 有必要吗?这背后的原因是什么?

\n
\n\n

我相信上述解释也应该解释当表中已有一条记录时您对第二种情况提出的问题。但无论如何我都会尝试解释它。

\n\n

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

\n\n

希望这能回答这个问题。

\n