Выбор ответа Senior Транзакции и изоляция обновлено

Как избежать deadlock при переводах

Вопрос

Два параллельных перевода: A→B и B→A. Каждая транзакция делает UPDATE баланса отправителя, потом получателя, и иногда ловит deadlock. Какое исправление правильное?

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

Выберите вариант, и откроется разбор.

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

Коротко

Правильный ответ: Блокировать строки в одном порядке: ... ORDER BY id FOR UPDATE. Deadlock возникает, когда транзакции берут одни и те же блокировки в разном порядке: первая держит A и ждёт B, вторая держит B и ждёт A.

Варианты ответа

  1. Поднять уровень изоляции до Serializable для обеих транзакций
  2. Увеличить deadlock_timeout, чтобы транзакции дождались друг друга
  3. Перейти на Read Uncommitted, чтобы транзакции не блокировали друг друга
  4. Блокировать строки в одном порядке: ... ORDER BY id FOR UPDATEправильный ответ

Разбор

Deadlock возникает, когда транзакции берут одни и те же блокировки в разном порядке: первая держит A и ждёт B, вторая держит B и ждёт A. Если все берут блокировки в едином порядке, цикл ожидания невозможен. deadlock_timeout (по умолчанию 1 с) это лишь время ожидания до запуска проверки на взаимоблокировку; его увеличение только отложит ошибку. Serializable не убирает блокировки строк при UPDATE и добавляет ошибки сериализации. Read Uncommitted в PostgreSQL работает как Read Committed, а блокировки записи от уровня изоляции не зависят. Повтор транзакции при ошибке 40P01 всё равно стоит иметь.

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

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

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