用于发送电子邮件的脚本死锁
Deadlock in script for sending emails
我有一个在后台发送电子邮件的脚本。脚本 运行s 并行地同时发送多封电子邮件。它基本上是这样工作的,混合了 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'
InnoDB 使用自动行级锁定。即使在仅插入或删除一行的事务中,您也可能会遇到死锁。那是因为这些操作并不是真正的“原子”;他们自动在插入或删除的行的(可能是几个)索引记录上设置锁。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht ml
我有一个在后台发送电子邮件的脚本。脚本 运行s 并行地同时发送多封电子邮件。它基本上是这样工作的,混合了 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'
InnoDB 使用自动行级锁定。即使在仅插入或删除一行的事务中,您也可能会遇到死锁。那是因为这些操作并不是真正的“原子”;他们自动在插入或删除的行的(可能是几个)索引记录上设置锁。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht ml