【问题标题】:Deadlock in script for sending emails用于发送电子邮件的脚本中的死锁
【发布时间】:2017-10-06 14:31:42
【问题描述】:

我有一个用于在后台发送电子邮件的脚本。该脚本并行运行以同时发送多封电子邮件。它的工作原理基本上是这样的,混合了 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'

【问题讨论】:

  • 发送所有电子邮件需要多长时间?您是否考虑过使用事务并锁定底层行进行更新?
  • 好吧,如果你有并行运行的脚本,它可能会发生很多。我们的解决方案是避免同时更新多行。它避免了这种错误。一些 mySQL 表类型正在使用事务,即使您没有指定它们。死锁只是因为没有两个进程试图同时修改相同的数据。当您并行化某些进程时,这种情况经常发生。
  • @mike 每封电子邮件都需要几秒钟的时间来发送。我考虑过这一点,但不明白为什么有必要这样做。
  • @RaphaëlGonçalves 我的查询都没有同时更新多行。第一个查询可能会这样做,但由于LIMIT 1,只更新了一行。
  • InnoDB 使用自动行级锁定。即使在仅插入或删除单行的事务的情况下,您也可能会出现死锁。那是因为这些操作并不是真正的“原子”;它们会自动对插入或删除的行的(可能是多个)索引记录设置锁定。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html

标签: php mysql database deadlock database-deadlocks


【解决方案1】:

InnoDB 使用自动行级锁定。即使在仅插入或删除单行的事务的情况下,您也可能会出现死锁。那是因为这些操作并不是真正的“原子”;它们会自动对插入或删除的行的(可能是多个)索引记录设置锁定。 dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht‌​ml

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多