Тупик в скрипте для отправки писем

У меня есть скрипт для отправки писем в фоновом режиме. Сценарий выполняется параллельно для одновременной отправки нескольких писем. Он работает в основном так, со смесью 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

Решение

InnoDB использует автоматическую блокировку на уровне строк. Вы можете получить взаимоблокировки даже в случае транзакций, которые просто вставляют или удаляют одну строку. Это потому, что эти операции на самом деле не «атомарные»; они автоматически устанавливают блокировки на (возможно, несколько) индексных записей вставленной или удаленной строки. dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.ht мл

0

Другие решения

Других решений пока нет …