# Дедлоки 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/uk/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/uk/kb/mysql-indeksy-optimizatsiya/) під умови WHERE в UPDATE і DELETE, щоб блокувався лише потрібний діапазон.
- Повторювати транзакцію автоматично під час помилки 1213 (deadlock), зазвичай двох-трьох спроб достатньо.

## Підсумок: чек-лист діагностики дедлоків

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

---

Джерело: https://www.zevshost.net/uk/kb/mysql-deadlock-blokirovki/
