Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Задачи

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

AI info

Карта сайта

Документы

Медиа

Назад
Вопрос про Postgres: deadlock, transaction, index

Какие причины могут привести к возникновению deadlock при конкурентных запросах к базе данных и как их предотвращать?

Этот вопрос проверяет понимание того, почему возникают дедлоки в реляционных БД и как проектировать запросы так, чтобы их избегать.

Короткий ответ

Deadlock появляется, когда два запроса блокируют ресурсы друг друга в пересекающемся порядке: один заблокировал строку A и ждёт строку B, а другой — наоборот. Причины: несогласованный порядок обновлений, долгие транзакции, отсутствие индексов, смешивание SELECT FOR UPDATE и UPDATE на разных путях. Чтобы избегать deadlock: соблюдать единый порядок обновления сущностей, делать транзакции короткими, использовать правильные индексы, избегать сканирования больших таблиц под блокировками и по возможности минимизировать конкурирующие UPDATE по одним и тем же строкам.

Длинный ответ

1. Что такое deadlock

Определение.
Deadlock (взаимная блокировка) — это ситуация, когда две или более транзакций ждут ресурсы, удерживаемые друг другом, и никто не может продолжать выполнение.

Пример:

  1. Транзакция T1: обновляет строку A → ждёт B.

  2. Транзакция T2: обновляет строку B → ждёт A.

Обе транзакции «висят», и Postgres завершает одну из них с ошибкой deadlock.

2. Причины возникновения deadlock

2.1. Несогласованный порядок обновлений

Классическая проблема:

  1. В одном месте кода обновляем (account_1 → account_2).

  2. В другом — (account_2 → account_1).

При больших нагрузках это почти гарантированно даёт дедлок.

2.2. Долгие транзакции

Если транзакция держит блокировку слишком долго:

  • Другие запросы накапливаются.

  • Вероятность циклической блокировки растёт.

Часто это связано с:

  • Долгими вычислениями внутри транзакции.

  • Большими SELECT без индекса.

  • Ненужными UPDATE.

2.3. SELECT FOR UPDATE без индекса

Если запрос:

SELECT * FROM accounts WHERE status = 'active' FOR UPDATE;

и нет индекса по статусу, Postgres делает seq scan и блокирует очень много строк → растёт шанс дедлока.

2.4. Асинхронный код + транзакции

Если приложение в асинхронном режиме делает несколько параллельных запросов в одной транзакции, это тоже может вызвать deadlock.

2.5. Конкурентный UPDATE одних и тех же строк

Если два сервиса обновляют один и тот же баланс/заказ/запись — конфликт почти неизбежен.

3. Как предотвращать deadlock

3.1. Единый порядок блокирования ресурсов

Главное правило:

  • Всегда обновлять записи в одинаковом порядке.

Например, при переводе денег:

  1. Определяем порядок: обновляем сначала аккаунт с меньшим ID, затем с большим.

  2. Тогда независимо от направления перевода запросы блокируют строки в одном порядке.

Python (псевдокод)

first, second = sorted([src_account, dst_account])
update(first)
update(second)

3.2. Короткие транзакции

Принципы:

  1. Транзакции содержат только операции с БД.

  2. Нет сложных расчётов внутри.

  3. Нет внешних API-вызовов.

  4. Нет долгого ожидания.

3.3. Использование корректных индексов

Если запрос делает:

SELECT ... FOR UPDATE WHERE user_id = 123;

то индекс по user_id обязателен.

Без индекса Postgres может заблокировать сотни ненужных строк.

3.4. Минимизировать избыточные UPDATE

Правильно:

  • UPDATE только если значение поменялось.

Неправильно:

  • ALWAYS UPDATE, даже если данные не меняются → создаются ненужные блокировки.

3.5. Оптимистическая блокировка

Вместо UPDATE → SELECT FOR UPDATE → UPDATE можно:

  1. Прочитать запись.

  2. Попробовать обновить с WHERE version = X.

  3. Если 0 обновлений — повторить.

Это снижает количество тяжёлых блокировок.

3.6. Логика «одна строка — один владелец»

Если на уровне приложения можно добиться, чтобы только один сервис обновлял ресурс — дедлоки исключаются.

Пример:

  • Балансы обновляются только через один сервис, а не всеми микросервисами.

4. Вывод

Deadlock возникает при неправильном порядке блокировок, долгих транзакциях, отсутствующих индексах и конкурирующих UPDATE. Чтобы их избежать, нужно согласовать порядок обновлений, минимизировать область транзакций, правильно индексировать запросы и по возможности уменьшать конкуренцию. Более продвинутые техники включают оптимистическую блокировку и перераспределение ответственности между сервисами.

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.

Уровень

  • Рейтинг:

    5

  • Сложность:

    7

Навыки

  • Postgres

    Postgres

Ключевые слова

#deadlock

#transaction

#index

Подпишись на Python Developer в телеграм

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.