MySQL返回带有插入行的死锁,FK被锁定'用于更新'

时间:2010-04-01 11:47:48

标签: mysql deadlock

我的mysql事务中出现死锁错误。

我的情况的简单例子:

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. 在my.cnf中使用innodb_locks_unsafe_for_binlog
  3. 锁定(用于更新)插入
  4. 之前的Thread2中的表A.

    还有其他解决方案吗?

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分: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;
 }  
 }

希望这可能会给你一些好主意。

答案 2 :(得分: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将包含两行,如下所示:

  1. 1000'Hello world''2011-06-11 19:23:15'
  2. 1000'Hello world2''2011-06-11 19:23:20'
  3. 您描述的情况仅在B.NAME是唯一索引且您尝试插入相同值时才会发生。第一个插入等待释放A.ID索引,因为B.NAME的重复值不会发生。