【问题标题】:MySQL gap lock behavior isn't in line with expectationMySQL 间隙锁行为不符合预期
【发布时间】:2020-12-11 12:18:52
【问题描述】:

问题描述:

在mysql 8.0中,我启动一个事务并执行SELECT * FROM child WHERE id > 1000 FOR UPDATE;(事务一),然后我启动另一个事务并执行update child set id = id+10 where id = 101;(事务二),child中有一行id = 101 ,但是这个事务二被阻止了。

如果我执行update child set id = id+10 where id = 102;(事务三)并且表中没有id = 102的行,事务三不会被阻塞,可以成功执行。

据我所知,mysql 8.0 gap lock只会锁定id大于1000的行,但是在第二个事务中,row id是101且不大于1000,所以两个事务不会相互冲突。那么为什么事务二会被事务一阻塞呢?

其他详情如下:

  1. child结构:

CREATE TABLE `child` (\n `id` int NOT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

  1. child中的所有数据:
mysql root@localhost:test> select * from child;
+------+
| id   |
+------+
| 90   |
| 101  |
| 105  |
| 106  |
| 109  |
| 111  |
| 1007 |
+------+
7 rows in set
  1. 所有 mysql 8.0 配置均为默认配置。
  2. 这两个事务是并行的。

【问题讨论】:

  • 然后我开始了另一个事务这个事务是并行的还是嵌套的?
  • 锁定行为将取决于您的数据,因此请添加有关它的详细信息。具体来说:您的表中是否有一个位于间隙中的 id(例如从 112 到 1001 的值)?如果不是,则该行为是预期的。 (如果您仍然想知道原因,我们可以详细说明,但需要确认您的数据)。
  • 我在上面添加了必要的细节,非常感谢您的回答。
  • 当您将 101 更新为 111 时,表中的第 111 行会导致您违反主键,但是,您的示例数据证实了我关于间隙锁覆盖区域的一般假设更新将放置行。

标签: mysql locking innodb


【解决方案1】:

简化一点,lock 将始终附加到现有对象,例如一行。

假设您的表中有 ID 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 之间有另一行,则间隙锁可以从那里开始,您的更新事务不必等待。

实际上,这是一个问题,尤其是对于小型表(或特定的更新/插入模式)。如果添加越来越多的行,随机更新命中一个“过度覆盖”间隙的概率将会降低。

【讨论】:

    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 2018-01-08
    • 2017-01-27
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多