我有一个在后台发送电子邮件的脚本。该脚本并行运行以同时发送多封电子邮件。它基本上是这样的,混合使用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
。执行最后一个查询时发生了这种情况。我执行第一个查询时没有看到它发生。谁能理解在这种情况下如何发生死锁?可能是第一个查询和最后一个查询以相反的顺序锁定StatusId
和TransmissionId
吗?但我认为第一个查询不需要锁定TransmissionId
,也不认为最后一个查询需要锁定StatusId
。我怎样才能找到它,我该如何解决?
还有另一个查询也可能发挥作用。每当有人打开电子邮件时,都会运行以下查询:
/* Selector is UNIQUE */
UPDATE transmission SET
OpenCount=OpenCount+1
WHERE Selector = 'barfoo'
答案 0 :(得分:0)
InnoDB使用自动行级锁定。即使只是插入或删除单行的事务,您也可能会遇到死锁。那是因为这些操作并非真正的“原子”;它们会自动设置对插入或删除的行的(可能是几个)索引记录的锁定。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht毫升