在mysql中重现这种死锁

时间:2012-08-30 23:31:50

标签: mysql foreign-keys deadlock

我从SHOW ENGINE INNODB STATUS获得了信息

    *** (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 

船上有一个FK,指的是订单主键。

我认为T2已经持有x-lock,为什么它仍然需要等待x-lock。

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

感谢。

1 个答案:

答案 0 :(得分:7)

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

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

  

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

     

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

     
      
  1. 来自事务1集的锁定,
  2.   
  3. 来自事务2集的锁定,
  4.   
  5. 请求的事务1的X锁定,被事务2的S锁阻止,
  6.   
  7. 请求事务2的X锁定,被事务1的S锁阻止
  8.   

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

UPDATE <parent row> SET count = count + 1;
INSERT <child row>; /* if the INSERT fails, roll back the trx */

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

SELECT <parent row> FOR UPDATE;
INSERT <child row>; /* if the INSERT fails, roll back the trx */
UPDATE <parent row> SET count = count + 1;