Ответ вслух Senior Транзакции и изоляция обновлено

Взаимоблокировки: как ловить и предотвращать

Вопрос

На проде в логах появились ошибки deadlock detected. Как вы будете разбираться и что сделаете, чтобы они ушли?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

Deadlock это когда транзакция A держит ресурс X и ждёт Y, а B держит Y и ждёт X. Сами они не разойдутся, поэтому PostgreSQL после deadlocktimeout (по умолчанию секунда) проверяет граф ожиданий и откатывает одну из транзакций с ошибкой 40P01, а в лог пишет оба процесса, запросы и виды блокировок.

Что должно прозвучать в ответе

  • Deadlock это цикл ожидания: транзакции держат блокировки и ждут друг друга

  • PostgreSQL проверяет цикл после `deadlock_timeout` (1 с по умолчанию) и откатывает одну из транзакций с ошибкой 40P01

  • В логе есть оба запроса и тип блокировок; включаю `log_lock_waits` для долгих ожиданий

  • Главное лечение: единый порядок взятия блокировок, короткие транзакции, блокировка заранее через `SELECT ... FOR UPDATE` в порядке ключа

  • Приложение должно уметь повторять транзакцию при 40P01 и 40001

Что могут спросить следом

  • Почему внешние ключи могут участвовать в deadlock?
  • Чем FOR UPDATE отличается от FOR NO KEY UPDATE?
  • Что даёт lock_timeout?

Разбор

Deadlock это когда транзакция A держит ресурс X и ждёт Y, а B держит Y и ждёт X. Сами они не разойдутся, поэтому PostgreSQL после deadlock_timeout (по умолчанию секунда) проверяет граф ожиданий и откатывает одну из транзакций с ошибкой 40P01, а в лог пишет оба процесса, запросы и виды блокировок. Разбор начинаю с лога: какие запросы и какие строки. Типичные причины: обновление одних строк в разном порядке (переводы A→B и B→A, пакетные обновления без сортировки), длинные транзакции с внешними вызовами внутри. Лечение: брать блокировки в едином порядке, например ORDER BY id FOR UPDATE, держать транзакции короткими, не ходить в сеть внутри транзакции. Для долгих ожиданий включаю log_lock_waits, для DDL выставляю lock_timeout. И в любом случае клиент должен уметь повторить транзакцию при 40P01 и 40001.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме