用于发送电子邮件的脚本中的死锁

时间:2017-10-06 14:31:43

标签: php mysql database deadlock database-deadlocks

我有一个在后台发送电子邮件的脚本。该脚本并行运行以同时发送多封电子邮件。它基本上是这样的,混合使用MySQL和PHP:

/* TransmissionId is a PRIMARY KEY */
/* StatusId is a FOREIGN KEY */
/* Token is UNIQUE */

/* Pick a queued (StatusId=1) transmission and set it to pending (StatusId=2) */
/* This is a trick to both update a row and store its id for later retrieval in one query */
SET @Ids = 0;
UPDATE transmission
SET StatusId=IF(@Ids := TransmissionId,2,2), LatestStatusChangeDate=NOW()
WHERE StatusId = 1
ORDER BY TransmissionId ASC
LIMIT 1;

/* Fetch the id of the picked transmission */
$Id = SELECT @Ids;

try {
    /* Fetch the email and try to send it */
    $Email = FetchEmail($Id);
    $Email->Send();

    /* Set the status to sent (StatusId=3) */
    $StatusId = 3;
} catch(Exception $E) {
    /* The email could not be sent, set the status to failed (StatusId=4) */
    $StatusId = 4;
} finally {
    /* Save the new transmission status */
    UPDATE transmission
    SET StatusId=$StatusId, LatestStatusChangeDate=NOW(), Token='foobar'
    WHERE TransmissionId = $Id;
}

问题是我有时会陷入僵局:SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction。执行最后一个查询时发生了这种情况。我执行第一个查询时没有看到它发生。谁能理解在这种情况下如何发生死锁?可能是第一个查询和最后一个查询以相反的顺序锁定StatusIdTransmissionId吗?但我认为第一个查询不需要锁定TransmissionId,也不认为最后一个查询需要锁定StatusId。我怎样才能找到它,我该如何解决?

修改

还有另一个查询也可能发挥作用。每当有人打开电子邮件时,都会运行以下查询:

/* Selector is UNIQUE */
UPDATE transmission SET
OpenCount=OpenCount+1
WHERE Selector = 'barfoo'

1 个答案:

答案 0 :(得分:0)

InnoDB使用自动行级锁定。即使只是插入或删除单行的事务,您也可能会遇到死锁。那是因为这些操作并非真正的“原子”;它们会自动设置对插入或删除的行的(可能是几个)索引记录的锁定。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht毫升