# Взаимоблокировки (deadlock) _Блокировки · PostgreSQL Knowledge Base_ **TL;DR:** Deadlock - цикл в графе ожидания: каждая транзакция ждёт ту, что ждёт её. PostgreSQL находит его через deadlock_timeout (1 с) и откатывает одну транзакцию с ошибкой 40P01. Лечится единым порядком блокировок. Очередь блокировок честная, поэтому обычное ожидание рано или поздно разрешается. Исключение - когда дождаться нельзя в принципе: A держит строку 1 и хочет строку 2, B держит строку 2 и хочет строку 1. Это взаимоблокировка, и сама она не рассосётся. ## Граф ожидания Вершины - транзакции, ребро `A → B` значит «A ждёт то, что держит B». Пока это цепочки и деревья, всё разрешается. Цикл (`T1 → T2 → T1`) означает вечное ожидание. Обнаружение deadlock - это поиск цикла. ## Как находится Строить граф на каждую блокировку дорого, а почти все ожидания разрешаются сами. Поэтому при постановке в очередь взводится таймер `deadlock_timeout` (по умолчанию 1 с). Если за это время блокировку не выдали - запускается детектор и ищет цикл. Нашёл - откатывает жертву; нет - ждёт дальше. Отсюда задержка между тупиком и его разрывом ≈ 1 с. ## Сообщение ``` ERROR: deadlock detected DETAIL: Process 412 waits for ShareLock on transaction 1033; blocked by process 410. Process 410 waits for ShareLock on transaction 1034; blocked by process 412. CONTEXT: while updating tuple (0,5) in relation "bookings" ``` SQLSTATE - `40P01`. Жертва получает полный `ROLLBACK` (вся транзакция, не последний запрос), вторая продолжает. Счётчик `pg_stat_database.deadlocks` растёт на единицу. ## Неочевидные источники Двух явных UPDATE не нужно. Один `UPDATE` на несколько строк в разном порядке обхода даёт тот же цикл (порядок задаёт план, не порядок в `IN`). Перекрёстные вставки по внешнему ключу - тоже. ## Как избежать - единый порядок блокировок (`ORDER BY id` в `FOR UPDATE`) делает граф ациклическим - цикл становится невозможен; - короткие транзакции сужают окно пересечения; - ретрай на `40P01`: deadlock штатен, приложение обязано повторять. В отличие от [idle-in-transaction-cascade](/courses/postgres/kb/idle-in-transaction-cascade.md), deadlock разрывает сам сервер. За очередью блокировок стоит механизм из [relation-locks](/courses/postgres/kb/relation-locks.md) и [row-locks](/courses/postgres/kb/row-locks.md). ## Команды ```sql SELECT deadlocks FROM pg_stat_database WHERE datname = current_database(); ``` Сколько взаимоблокировок случилось в базе ```sql SET deadlock_timeout = '1s'; ``` Через сколько ждать перед запуском детектора циклов ```sql SELECT * FROM bookings WHERE book_ref IN ('000001','000002') ORDER BY book_ref FOR UPDATE; ``` Блокировать строки в едином порядке - профилактика deadlock ## См. также - [Тяжёлые блокировки отношений](/courses/postgres/kb/relation-locks.md) - [Блокировки строк](/courses/postgres/kb/row-locks.md) - [idle-in-transaction и каскад блокировок](/courses/postgres/kb/idle-in-transaction-cascade.md)