Дедлоки и блокировки

Тема дорожной карты · PostgreSQL

Взаимоблокировки возникают в PostgreSQL, когда две или более транзакции удерживают блокировку, которую ожидает другая транзакция, создавая цикл зависимостей, который при отсутствии вмешательства заблокировал бы их навсегда. PostgreSQL автоматически обнаруживает взаимоблокировки, периодически проверяя граф зависимостей блокировок — этим управляет параметр deadlock_timeout (по умолчанию 1 секунда) — и разрешает их, прерывая одну из транзакций с ошибкой ERROR: deadlock detected, что инициирует ACID-совместимый откат этой транзакции. Типичными источниками взаимоблокировок в приложениях реляционных баз данных являются: несогласованный порядок блокировки строк между параллельными транзакциями, использование явного SELECT ... FOR UPDATE без последовательного порядка доступа, а также массовые операции UPDATE, получающие блокировки строк в недетерминированном порядке. Для предотвращения взаимоблокировок в PostgreSQL необходимо получать блокировки в согласованном порядке, поддерживать транзакции короткими и сосредоточенными на оптимизации запросов, а также использовать консультативные блокировки (pg_advisory_lock) для координации на уровне приложения, когда стандартной блокировки строк недостаточно.

Как это работает

Дедлоки и блокировки оборачивают несколько операторов как одну атомарную единицу: BEGIN; ... ; COMMIT; (или ROLLBACK). ACID-гарантии: atomicity (всё или ничего), consistency (constraints выполнены), isolation (конкурентные txn выглядят последовательно на выбранном уровне), durability (committed = записано). Уровни изоляции: Read Committed (дефолт), Repeatable Read, Serializable (сильнейший, может требовать retry). Postgres использует MVCC: читатели не блокируют писателей и наоборот.

Когда применять

Оборачивайте multi-row бизнес-операции в транзакции — частичное состояние после крэша хуже, чем сбой. Serializable — для денежной логики, где два конкурентных update могут двойной счёт; иначе Read Committed достаточен и быстрее. SELECT ... FOR UPDATE — для явных row-локов. Savepoint (SAVEPOINT ...; ROLLBACK TO ...;) — для nested partial rollback внутри длинной транзакции.

Типичные ошибки

Ловушки Дедлоки и блокировки: долгие транзакции блокируют VACUUM (autovacuum не освобождает, пока snapshot жив — bloat растёт); idle-in-transaction соединения (баговый ORM не делает commit/rollback); Serializable повсюду без retry-обработки (получите криптичные 40001); медленный внешний IO (HTTP-вызовы) внутри транзакции (lock держится слишком долго). Ставьте idle_in_transaction_session_timeout.

Связанные понятия

Полезные ресурсы