我的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
的外键我看到了三个解决方案:
还有其他解决方案吗?
答案 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将包含两行,如下所示:
您描述的情况仅在B.NAME是唯一索引且您尝试插入相同值时才会发生。第一个插入等待释放A.ID索引,因为B.NAME的重复值不会发生。