问题描述:
在MySQL8.0中,我启动了一个事务并执行了一个事务(transaction one),然后启动了另一个事务并执行了id = 101
(transaction 2),并且表 child
,中有一行,但是这个事务2被阻塞了。
如果我执行update child set id = id+10 where id = 102;
(事务3),并且表中不存在行id = 102
,事务3将不会被阻塞,并且可以成功地执行。
就我而言,MySQL8.0 gap锁只锁定id大于1000的行,但在第二个事务中,行id为101,而不大于1000,因此这两个事务不会相互冲突。那么,为什么事务二被事务一阻塞呢?
详细信息如下:
child
结构:CREATE TABLE `child` (\n `id` int NOT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
child
中的所有数据mysql root@localhost:test> select * from child;
+------+
| id |
+------+
| 90 |
| 101 |
| 105 |
| 106 |
| 109 |
| 111 |
| 1007 |
+------+
7 rows in set
发布于 2020-12-16 11:59:39
简化一点,锁定将始终附加到现有的对象,例如行。
假设您的表中有in 90、101、105、106、109、110和1007。请注意,我将您的示例行111更改为110,否则,更新从101到111将因主键违规而失败,然后才会遇到锁问题。
如果MySQL需要为WHERE id > 1000
发出一个锁,它将不会(因为它不能)跟踪确切的值> 1000
。相反,它将接受与该值最近的现有对象(在您的示例中是id 110的行),并添加一个间隙锁,该锁覆盖从110到1006的空间。和另一个锁+间隙锁,在行与id 1007,涵盖空间从1007和更高。
是的,这涵盖了比需要更多的空间。但是这个锁包含了所需要的空间,这是重要的方面。
您的update child set id = id+10 where id = 101
现在将将行移动到锁定的空间中,因此必须等待现有锁的释放。因此,这实际上是预期的行为,因为MySQL是如何工作的。
如果在112到1000之间还有另一行,则可以从那里开始间隙锁,并且您的更新事务无需等待。
实际上,这是一个问题,特别是对于小表(或特定的更新/插入-模式)。如果添加了越来越多的行,随机更新击中“覆盖过大”的漏洞的可能性就会降低。
https://stackoverflow.com/questions/65251249
复制相似问题