在mysql中重现这种死锁

Ton*_*Zhu 1 mysql deadlock foreign-keys

我从SHOW ENGINE INNODB状态获得了信息

    *** (1) TRANSACTION: 
TRANSACTION 0 2799914, ACTIVE 1 sec, process no 4106, OS thread id 139808903796480 inserting
mysql tables in use 1, locked 1
LOCK WAIT 10 lock struct(s), heap size 1216, 7 row lock(s), undo log entries 3
MySQL thread id 4284, query id 2889649 localhost 127.0.0.1 test update
INSERT INTO shipping .....
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 436366 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799914 lock mode S locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
..........;

*** (2) TRANSACTION:
TRANSACTION 0 2799913, ACTIVE 1 sec, process no 4106, OS thread id 139808905824000 starting index read, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1216, 5 row lock(s), undo log entries 4
MySQL thread id 4290, query id 2889711 localhost 127.0.0.1 test Updating
UPDATE order
........
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 436366 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799913 lock_mode X locks rec but not gap
Record lock, heap no 14 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
..........

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 153737 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799913 lock_mode X locks rec but not gap waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
......

*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS 
Run Code Online (Sandbox Code Playgroud)

在船上有一个FK指的是订单主键.

我认为T2已经持有x-lock,为什么它还需要等待x-lock.

有人可以帮我在mysql中重现这种死锁吗?

谢谢.

Iva*_*yan 7

我不知道你的查询,但似乎你在子表中插入行,然后在父表中更新行.

如果这是真的,你在MySQL中遇到了这个问题:http://bugs.mysql.com/bug.php?id = 48652

如果在表上定义了FOREIGN KEY约束,则需要检查约束条件的任何插入,更新或删除都会在其查看的记录上设置共享记录级锁定以检查约束.InnoDB还在约束失败的情况下设置这些锁.

在第一张表的单个记录中,您有:

  1. 来自事务1集的S锁定,
  2. 从事务2集中锁定,
  3. 从请求的事务1中锁定X,由事务2的S锁定阻止,
  4. 来自事务2的X锁被请求,被来自事务1的S锁阻塞

可能的解决方案是首先更新父表,然后将行插入子表.假设我们需要在子行插入时增加一些计数器,然后查询将是:

UPDATE <parent row> SET count = count + 1;
INSERT <child row>; /* if the INSERT fails, roll back the trx */
Run Code Online (Sandbox Code Playgroud)

如果只想在插入子行后更新父行,可以使用FOR UPDATEstatement在父行上设置锁定:

SELECT <parent row> FOR UPDATE;
INSERT <child row>; /* if the INSERT fails, roll back the trx */
UPDATE <parent row> SET count = count + 1;
Run Code Online (Sandbox Code Playgroud)