避免在尝试锁定时发现MySQL的死锁;尝试重启事务'

时间:2011-11-07 16:11:11

标签: php mysql deadlock database-deadlocks

我有时会遇到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的地方。理论上它们永远不应该重叠。

我想知道是否有正确的方法来避免这些死锁?

是否会发生死锁,因为一个事务在选择/更新时锁定表太长而另一个进程正在尝试执行相同的事务并且只是超时?

非常感谢任何帮助

3 个答案:

答案 0 :(得分:3)

当两个或多个进程以锁定资源重叠但以不同顺序发生的方式请求锁定时会发生死锁,因此每个进程都在等待另一个进程锁定的资源,而其他进程正在等待对于原始进程已打开的锁定。

在现实世界中,考虑一个施工现场:你有一把螺丝刀和一个螺丝钉。两名工人需要用螺丝钉驱动。工人#1抓住螺丝刀,工人#2抓住螺丝刀。工人#1也去抓螺丝,但不能,因为它是由工人#2持有。工人#2需要螺丝刀,但由于工人#1持有螺丝刀而无法获得螺丝刀。所以现在他们陷入僵局,无法继续前进,因为他们拥有他们需要的2个资源中的1个,而且他们都没有礼貌并且“退后一步”。

鉴于您发生了事务外更改,您的更新/删除中的一个(或多个)可能与您在事务中保留的锁定区域重叠。

答案 1 :(得分:2)

您可能希望在启动事务之前尝试LOCK TABLES,从而确保您可以显式控制表。锁将一直等到特定表上的所有活动都完成。

答案 2 :(得分:1)

我认为网上的每个人都已经很好地解释了僵局。 Mysql提供了非常好的日志来检查所有最后一次死锁发生了什么 查询在那时被卡住了。 检查此mysql documentation page并搜索LATEST DETECTED DEADLOCK 它是一个很好的日志,帮助发现了许多微妙的僵局。