# Дедлоки InnoDB: диагностика блокировок и их устранение

> Почему возникают дедлоки в InnoDB, как читать SHOW ENGINE INNODB STATUS и какие правила в коде снижают число блокировок.

## Что такое дедлок в InnoDB

Дедлок — это взаимная блокировка: транзакция A ждёт освобождения строки, занятой транзакцией B, а транзакция B в этот момент ждёт строку, занятую транзакцией A. Ни одна из них не может продолжить работу, и InnoDB через встроенный детектор сам обрывает одну из транзакций с ошибкой Deadlock found when trying to get lock. Это не сбой сервера, а штатный механизм защиты от зависания.

В отличие от обычной блокировки, которая просто заставляет транзакцию подождать, дедлок требует вмешательства приложения: код должен поймать ошибку и повторить транзакцию заново.

## Разделяемые и монопольные блокировки строк

InnoDB блокирует не таблицу целиком, а отдельные строки и промежутки между ними. Понимание типов блокировок помогает читать вывод диагностики.

- Shared lock (S) — разделяемая блокировка, ставится при SELECT ... FOR SHARE, разрешает другим транзакциям тоже читать строку.
- Exclusive lock (X) — монопольная блокировка, ставится при UPDATE, DELETE и SELECT ... FOR UPDATE, запрещает любой доступ другим транзакциям.
- Gap lock — блокировка промежутка между значениями индекса, нужна для предотвращения фантомных строк при повторяемом чтении.

## Как найти последний дедлок на сервере

Информация о последнем зафиксированном дедлоке хранится в статусе движка InnoDB. Команда выводит обе транзакции, их блокировки и то, какую из них сервер откатил.

```
SHOW ENGINE INNODB STATUS\G
```

В блоке LATEST DETECTED DEADLOCK видно текст запроса каждой транзакции, список удерживаемых и ожидаемых блокировок, а в конце — строку WE ROLL BACK TRANSACTION с номером отменённой транзакции. Разбор плана этих запросов через [EXPLAIN](https://www.zevshost.net/ru/kb/mysql-explain-plan-zaprosa/) часто показывает, что оба запроса идут по разным индексам к одним и тем же строкам.

## Таблица типовых причин дедлоков

| Причина | Пример | Что делать |
| --- | --- | --- |
| Разный порядок блокировок | Транзакция A обновляет строки 1→2, транзакция B — 2→1 | Зафиксировать единый порядок изменения строк |
| Долгая транзакция | Открытая транзакция ждёт ответа внешнего сервиса | Не держать транзакцию открытой во время сетевых вызовов |
| Отсутствие индекса | UPDATE идёт по неиндексированному полю | Добавить индекс, чтобы блокировался диапазон меньше |

## Частые причины дедлоков в реальных приложениях

На практике большинство дедлоков повторяются по одному и тому же сценарию.

- Два обработчика заказа одновременно списывают остаток товара в разном порядке строк.
- Триггер на таблице делает вставку в другую таблицу, создавая скрытую вторую блокировку.
- Массовое обновление без сортировки строк по первичному ключу — порядок блокировок становится случайным.
- Долгая транзакция удерживает блокировку, пока приложение ждёт ответа от очереди или внешнего API.

## Как снизить число дедлоков

Полностью убрать дедлоки нельзя — это часть нормальной работы MySQL под нагрузкой, но их частоту снижают несколько приёмов.

```
-- Сортировка строк по первичному ключу перед изменением
SELECT id FROM orders WHERE status = 'new' ORDER BY id FOR UPDATE;
```

- Обновлять строки в одном и том же порядке во всех местах кода.
- Держать транзакции короткими: не выполнять внешние вызовы между BEGIN и COMMIT.
- Добавлять [индексы](https://www.zevshost.net/ru/kb/mysql-indeksy-optimizatsiya/) под условия WHERE в UPDATE и DELETE, чтобы блокировался только нужный диапазон.
- Повторять транзакцию автоматически при ошибке 1213 (deadlock), обычно двух-трёх попыток достаточно.

## Итог: чек-лист диагностики дедлоков

- Прочитать LATEST DETECTED DEADLOCK в SHOW ENGINE INNODB STATUS.
- Определить порядок блокировок в обеих транзакциях.
- Проверить, есть ли подходящий индекс под условия запросов.
- Сократить транзакцию до минимально необходимых операций.
- Добавить в код повтор транзакции при ошибке 1213.

---

Источник: https://www.zevshost.net/ru/kb/mysql-deadlock-blokirovki/
