MySQL锁定重复键错误

时间:2016-06-22 15:52:00

标签: mysql concurrency transactions locking

来自docs

  

如果发生重复键错误,则在重复索引上使用共享锁   记录已设定。这种共享锁的使用可能导致死锁   有多个会话试图插入相同的行,如果另一个   会话已经有一个独占锁。如果是另一个,可能会发生   session删除该行。

使用文档中的示例,

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

CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;

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

第1节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第二节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第3节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第1节:

ROLLBACK;
  

会话1的第一个操作获取了一个独占锁   行。会话2和3的操作都会产生重复密钥   错误,他们都请求该行的共享锁。第1次会议   回滚,它释放行上的排他锁和排队   授予会话2和3的共享锁请求。在此刻,   会话2和3死锁:两者都不能获得排他锁   该行是因为另一方持有共享锁。

我有一些问题:

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

2)为什么T2和T3采用共享锁?插入过程中共享锁如何进入画面?

3 个答案:

答案 0 :(得分:0)

  1. 对于会话2和3:INNODB在执行前评估查询并确定该行是否已锁定(行为i = 1),事务将等待解锁隐含行。

    在第一个会话中插入后以及在会话2和3中运行插入后执行SHOW ENGINE INNODB STATUS时:

    ------------
    TRANSACTIONS
    ------------
    Trx id counter 2079155
    Purge done for trx's n:o < 2079150 undo n:o < 0 state: running but idle
    History list length 594
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 2079154, ACTIVE 21 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
    MySQL thread id 540, OS thread handle 0x7ff989386700, query id 1683 localhost root update
    INSERT INTO t1 VALUES(1)
    ------- TRX HAS BEEN WAITING 21 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 4190 page no 3 n bits 72 index `PRIMARY` of table `temp`.`t1` trx id 2079154 lock mode S locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
     0: len 4; hex 80000001; asc     ;;
     1: len 6; hex 0000001fb9af; asc       ;;
     2: len 7; hex 9c000001d30110; asc        ;;
    
    ------------------
    ---TRANSACTION 2079153, ACTIVE 43 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
    MySQL thread id 541, OS thread handle 0x7ff989355700, query id 1680 localhost root update
    INSERT INTO t1 VALUES(1)
    ------- TRX HAS BEEN WAITING 43 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 4190 page no 3 n bits 72 index `PRIMARY` of table `temp`.`t1` trx id 2079153 lock mode S locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
     0: len 4; hex 80000001; asc     ;;
     1: len 6; hex 0000001fb9af; asc       ;;
     2: len 7; hex 9c000001d30110; asc        ;;
    

    解锁行后(从会话1回滚)会话2将收到错误:ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction,会话3:Query OK, 1 row affected

  2. 会话2和3没有共享锁,它们在队列中获取一个,因为第一个会话的锁是独占的,需要等待。在这种情况下,会话3将获得一个并插入记录。

      

    共享锁:一种锁,允许其他事务读取锁定的对象,并获取其上的其他共享锁,但是   不写信给它。与排他锁相反。

         

    独占锁:一种阻止任何其他事务锁定同一行的锁。取决于事务隔离   等级,这种锁可能会阻止其他交易写入   到同一行,或者也可能阻止其他交易阅读   同一排。默认的InnoDB隔离级别,REPEATABLE READ,   通过允许事务读取行来实现更高的并发性   拥有独占锁,这种技术称为一致读。

答案 1 :(得分:0)

  

1)insert查询对它所在的行进行独占锁定   插入。因此,假设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都被授予共享锁。此时,两者都尝试获取独占锁以完成插入。但是,没有人可以,因为另一个人已经持有共享锁。因此,不会授予独占锁定,并且它们会死锁。

答案 2 :(得分:0)

问题2:

2)为什么T2和T3采用共享锁?插入过程中共享锁如何进入画面?

它需要锁定现有条目,以便后续尝试插入重复记录失败。