Что такое дедлоки?
Что проверяют
Понимаете ли, где в PHP вообще возможен дедлок. Классическая схема это отдельный процесс на запрос без разделяемых потоков, поэтому взаимная блокировка приезжает из базы или из внешних замков, и ждут разговора про InnoDB, а не про мьютексы.
Как отвечать
Дедлок это круговое ожидание блокировок: первая транзакция держит строку и ждёт вторую, вторая наоборот. InnoDB не ждёт бесконечно, детектор находит цикл и снимает более дешёвую транзакцию с ошибкой 1213, откатывая её целиком. Отличать это надо от таймаута ожидания блокировки, где снимается только текущий запрос.
Отсюда правильная стратегия: дедлок не устраняется полностью, он обрабатывается повтором всей транзакции, ограниченное число раз, со случайной паузой.
for ($try = 1; $try <= 3; $try++) {
try {
$pdo->beginTransaction();
// работа
$pdo->commit();
break;
} catch (PDOException $e) {
$pdo->rollBack();
if ($e->errorInfo[1] !== 1213 || $try === 3) { throw $e; }
usleep(random_int(50, 250) * 1000);
}
}Частоту снижают одинаковым порядком обращения к строкам, короткими транзакциями и индексом под условие обновления: без индекса блокируется лишний диапазон, и пересечения возникают там, где логически их нет.
Где спотыкаются
Рассказывают про потоки и мьютексы. В обычном процессе их нет, зато есть блокировки файлов и распределённые замки в Redis, и вот там порядок захвата продумывать действительно надо.
Повторяют только упавший запрос. После ошибки транзакция уже откачена целиком, продолжать её нечем: повторять надо с открытия транзакции, иначе данные уедут наполовину.
Ставят повтор и на этом успокаиваются. Пока не посмотреть последний дедлок в выводе состояния движка, причина останется, и под нагрузкой повторы начнут множить саму нагрузку.