Прежде всего, я не вижу, как я могу получить какой-либо тупик вообще, так как я не использую явное блокирование, есть только одна таблица, есть отдельный процесс для вставки, выбора и обновления строк, только одна строка вставляется или обновляется за раз, и каждый процесс выполняется редко (возможно, один раз в минуту).
Это очередь электронной почты:
CREATE TABLE `emails_queue` (
`id` varchar(40) NOT NULL,
`email_address` varchar(128) DEFAULT NULL,
`body` text,
`status_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` enum('pending','inprocess','sent','discarded','failed') DEFAULT NULL,
KEY `status` (`status`),
KEY `status_time` (`status`,`status_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Процесс генерации, в ответ на какое-то действие пользователя, но примерно каждые 90 секунд, вставляет в таблицу, устанавливая статус на "ожидающий".
Есть процесс мониторинга, который каждую минуту проверяет, что количество "ожидающих" и "неудачных" писем не является чрезмерным. Это занимает меньше секунды, чтобы бежать и никогда не беспокоило меня.
Каждую минуту процесс отправки захватывает все ожидающие письма. Он циклически проходит и по одному электронному адресу заставляет его статус "inprocess", пытается отправить его и, наконец, устанавливает свой статус соответственно "отправлен", "отбрасывается" (у него есть причины для принятия решения по электронной почте не должны выходить ) или "не удалось" (отклонено системой SMTP).
Заявление о настройке статуса необычно.
UPDATE emails_queue SET status=?, status_time=NOW() WHERE id=? AND status = ?
То есть, я только обновляю статус, если текущее состояние уже есть то, что я считаю. Перед этим механизмом я случайно начал два процесса отправки, и каждый из них попытается отправить тот же адрес электронной почты. Теперь, если это произойдет, один процесс успешно переместит электронное письмо с "ожидающего" на "inprocess", но второй будет обновлять нулевые строки, реализовать там проблему и пропустить эту электронную почту.
Проблема в том, что примерно за один раз в 100 обновление полностью завершается! Я получаю com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
WTH?
Это единственная таблица и только запрос, к которому это происходит, и это происходит только в процессе производства (чтобы максимально затруднить его изучение).
Единственные две вещи, которые кажутся совершенно необычными: (1) обновление столбца, участвующего в предложении WHERE, и (2) (неиспользованное) автоматическое обновление состояния_time.
Я ищу любые предложения или диагностические методы.