2010-04-01 62 views
7

我在我的mysql事务中遇到死锁错误。MySQL返回死锁插入行和FK被锁定'更新'

我的情况简单的例子:

Thread1 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
1 row in set (0.00 sec) 

Thread2 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread2 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world'); 
[Hangs] 

Thread1 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world2'); 
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

Thread2 > 
Query OK, 1 row affected (10.00 sec) 

B.AID是外键指A.ID

我看到三种解决方案:

  1. 捕获死锁错误代码并重试查询。
  2. 使用innodb_locks_unsafe_for_binlog在my.cnf
  3. 锁(用于更新)表A中的线程2插入

之前有没有其他解决办法?

+0

我可以;看看为什么这将是一个僵局,你的标题提到FK的。 A和B之间是否有外键关系,你没有提及 – Elemental 2010-04-01 14:16:12

+0

@Elemental:是的,有一个外键。 OP提到这一点,但有点神秘。我修改了帖子来说明问题。 – 2010-04-02 08:08:46

回答

1

我不知道这个例子的代码是什么,但是对于两个线程来说可能都值得使用LOCK IN SHARE MODE,因为你实际上并没有更新这个行本身。如果你必须使用LOCK FOR UPDATE,我会认为锁定其他线程将是唯一的逻辑路径。

另外,如果你打开离开MySQL,我发现PostgreSQL有更好的解决死锁问题。在某些情况下,每当在> 1线程上运行相同的脚本时,我发现MySQL会死锁。 PostgreSQL中相同的脚本可以处理任何数量的并行线程。

+0

'锁定共享模式'在我的项目中不可接受; ( – user306814 2010-04-07 09:44:12

+0

+1。虽然它看起来不是最好的解决方案,但它比LOCK FOR UPDATE好得多。 – 2010-04-08 09:19:54

0

基于从mysql high performance blog.

一个功能,我能够实现以下僵局PHP处理代码:

/* maximum number of attempts for deadlock */ 
$MAX_ATTEMPS = 20; 

/* query */ 
$sql = "INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world')"; 

/* current attempt counter */ 
$current = 0; 

/* try to query */ 
while ($current++ <$MAX_ATTEMPS) 
{ 
    $result = mysql_query($sql); 
    if(!$result && (mysql_errno== '1205' || mysql_errno == '1213' )) 
     continue; 
    else 
     break; 
} 
} 

希望这可能会给你一些好的想法。

+0

这是很好的解决方案,但我想要找到其他路径 – user306814 2010-04-07 09:43:14

+0

当事务中只有INSERT语句时,这可能是一个很好的解决方案。但总的来说,这是不可接受的,因为事务开始后你必须重复整个操作,这可能会跨越几个函数(方法)。 – 2010-04-08 08:56:50

0

这里没有死锁,你使用什么版本的MySQL和什么隔离级别? 我得到了这些结果,添加时间戳列于表B:

Thread1 > BEGIN; 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
/* 0 rows affected, 1 rows found */ 

Thread2 > BEGIN; 

Thread2 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world', NOW()); 
[Hangs] 

-- after 5 seconds 

Thread1 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world2', NOW()); 
/* 1 rows affected, 0 rows found */ 

Thread1 > COMMIT; 

Thread2 > COMMIT; 

B就包含2行,看起来像:

  1. 1000 '世界,你好'“2011-06-11 19:23: 15'
  2. 1000'你好world2‘2011-06-11 19点23分二十秒’

您所描述的情况,只有当B.NAME是唯一索引的地方,你试图插入相同值。第一个插入等待A.ID索引被释放,这将不会因为B.NAME的值重复而发生。