MySQL在重复密钥错误中锁定

Pri*_*oel 5 mysql concurrency locking transactions

文档

如果出现重复键错误,则会在重复索引记录上设置一个共享锁。如果有多个会话试图插入同一行(如果另一个会话已经具有互斥锁),则使用共享锁可能会导致死锁。如果另一个会话删除该行,则会发生这种情况。

结合文档中的示例,

假设InnoDB表t1具有以下结构:

CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
Run Code Online (Sandbox Code Playgroud)

现在,假设三个会话按顺序执行以下操作:

第一场:

START TRANSACTION;
INSERT INTO t1 VALUES(1);
Run Code Online (Sandbox Code Playgroud)

第二场:

START TRANSACTION;
INSERT INTO t1 VALUES(1);
Run Code Online (Sandbox Code Playgroud)

第三节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);
Run Code Online (Sandbox Code Playgroud)

第一场:

ROLLBACK;
Run Code Online (Sandbox Code Playgroud)

会话1的第一个操作获取该行的排他锁。会话2和3的操作都导致重复键错误,并且都请求该行的共享锁。会话1回滚时,它将释放该行的排他锁,并为会话2和3排队等待共享锁请求。此时,会话2和3死锁:由于另一个持有共享锁,因此两个都无法获得该行的排他锁。

我有一些问题 :

1)insert查询对其插入的行进行排他锁。因此,假设T1正在插入第1行,它将锁定第1行。现在,当T2开始写入时,INNODB将在执行查询之前对查询进行评估,并发现它将要写入相同的PK(i = 1的行)让T2等待?还是将开始执行T2并发现它给出了重复的键错误或PK违反。

2)为什么T2和T3采取共享锁?插入期间如何看到共享锁?

Uni*_*One 1

1) 插入查询在其插入的行上获取排它锁。因此,假设 T1 在第 1 行上插入,它将锁定第 1 行。现在,当 T2 开始写入时,INNODB 会在执行查询之前评估该查询,并发现它将写入相同的 PK(i = 1 的行)让T2等待?或者它会开始执行 T2 并发现它给出重复密钥错误或 PK 违规。

我认为您正在简化术语/流程。解析查询之后、执行之前,需要获取必要的锁。正是在这一点上,确定:

  • 会话1获得排它锁,因为它正在插入并且没有其他锁
  • 会话 2 和 3 排队等待共享锁,因为排他锁已被会话 1 持有,并且会话 2 和 3 出现重复键错误

2)为什么T2和T3要获取共享锁?插入期间共享锁如何出现?

根据上面的内容,会话 2 和 3 会排队等待共享锁,因为它们存在重复键错误。但是,当会话 1 删除密钥并释放排他锁时,会话 2 和会话 3 都获得了共享锁。此时双方都尝试获取排它锁来完成插入。但两者都不能,因为另一个已经持有共享锁。因此,没有授予任何一个独占锁,并且它们陷入死锁。