Gymterview
middle

Что такое deadlock и как его избежать?

Deadlock (взаимная блокировка) — ситуация, когда две или более транзакции блокируют друг друга, каждая ожидая ресурс, удерживаемый другой. PostgreSQL автоматически обнаруживает deadlock и откатывает одну из транзакций.

Пример

Пример
Транзакция 1:                        Транзакция 2:
BEGIN;                               BEGIN;
UPDATE accounts SET balance = 900    UPDATE accounts SET balance = 1100
  WHERE id = 1;  -- блокирует id=1    WHERE id = 2;  -- блокирует id=2
UPDATE accounts SET balance = 1100   UPDATE accounts SET balance = 900
  WHERE id = 2;  -- ЖДЁТ id=2         WHERE id = 1;  -- ЖДЁТ id=1
-- DEADLOCK!

PostgreSQL обнаруживает deadlock через детектор с периодом deadlock_timeout (по умолчанию 1 секунда) и откатывает одну из транзакций с ошибкой ERROR: deadlock detected.

Способы избежать deadlock

1. Единый порядок обращения к ресурсам:

Пример
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = LEAST(1, 2);
UPDATE accounts SET balance = balance + 100 WHERE id = GREATEST(1, 2);
COMMIT;

2. SELECT FOR UPDATE с сортировкой:

Пример
BEGIN;
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

3. Минимизировать время транзакций — не выполнять долгие вычисления или вызовы внешних сервисов внутри транзакции.

4. NOWAIT или SKIP LOCKED:

Пример
SELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
SELECT * FROM task_queue WHERE status = 'pending'
ORDER BY id LIMIT 1 FOR UPDATE SKIP LOCKED;

5. Уменьшить уровень изоляцииREAD COMMITTED менее склонен к deadlock, чем SERIALIZABLE.

На собеседовании: главное правило предотвращения deadlock — единый порядок обращения к ресурсам. Если все транзакции блокируют строки в порядке возрастания ID, deadlock невозможен. Упомяните, что PostgreSQL обнаруживает deadlock автоматически, поэтому приложение должно уметь повторять откаченную транзакцию.