我有时会遇到mysql死锁错误:
'试图锁定时发现死锁;尝试重新启动事务'
我有一个队列表,其中多个php进程同时从表中选择行。但是,对于我想要的每个进程,每次获取都会获取一组唯一的行,因此我没有选择任何重叠的行。
所以我运行这个查询:(这是我得到死锁错误的查询)
$this->db->query("START TRANSACTION;");
$sql = " SELECT mailer_queue_id
FROM mailer_queues
WHERE process_id IS NULL
LIMIT 250
FOR UPDATE;";
...
$sql = "UPDATE mailer_queues
SET process_id = 33044,
status = 'COMPLETED'
WHERE mailer_queue_id
IN (1,2,3...);";
...
if($this->db->affected_rows() > 0) {
$this->db->query("COMMIT;");
} else{
$this->db->query("ROLLBACK;");
}
我也是:
同时向表中插入行(没有事务/锁定)
同时更新表中的行(没有事务/锁定)
同时从表中删除行(没有事务/锁定)
同样,我的更新和删除仅更新和删除分配了process_id的行...并且我执行我的事务“SELECT rows ... FOR UPDATE”是process_id = null的地方。理论上它们永远不应该重叠。
我想知道是否有正确的方法来避免这些死锁?
是否会发生死锁,因为一个事务在选择/更新时锁定表太长而另一个进程正在尝试执行相同的事务并且只是超时?
非常感谢任何帮助
答案 0 :(得分:3)
当两个或多个进程以锁定资源重叠但以不同顺序发生的方式请求锁定时会发生死锁,因此每个进程都在等待另一个进程锁定的资源,而其他进程正在等待对于原始进程已打开的锁定。
在现实世界中,考虑一个施工现场:你有一把螺丝刀和一个螺丝钉。两名工人需要用螺丝钉驱动。工人#1抓住螺丝刀,工人#2抓住螺丝刀。工人#1也去抓螺丝,但不能,因为它是由工人#2持有。工人#2需要螺丝刀,但由于工人#1持有螺丝刀而无法获得螺丝刀。所以现在他们陷入僵局,无法继续前进,因为他们拥有他们需要的2个资源中的1个,而且他们都没有礼貌并且“退后一步”。
鉴于您发生了事务外更改,您的更新/删除中的一个(或多个)可能与您在事务中保留的锁定区域重叠。
答案 1 :(得分:2)
您可能希望在启动事务之前尝试LOCK TABLES,从而确保您可以显式控制表。锁将一直等到特定表上的所有活动都完成。
答案 2 :(得分:1)
我认为网上的每个人都已经很好地解释了僵局。
Mysql提供了非常好的日志来检查所有最后一次死锁发生了什么
查询在那时被卡住了。
检查此mysql documentation page并搜索LATEST DETECTED DEADLOCK
它是一个很好的日志,帮助发现了许多微妙的僵局。