PostgreSQL LinuxWindowsсервер PostgreSQL

PostgreSQL: PostgreSQL сообщает о конфликте при повторной транзакции

  • Обновлено
  • Чтение ≈ 2 минуты

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

При повторении транзакции PostgreSQL может вернуть SQLSTATE 40001 serialization_failure или 23505 unique_violation. В отличие от взаимной блокировки это часто связано с повторным применением бизнес-операции либо уровнем изоляции; корректная стратегия учитывает уже выполненные внешние действия.

В этой статье
  1. Симптом и границы применимости
  2. Как отличить возможные причины
  3. Пошаговая диагностика и безопасное исправление
  4. Как проверить, что проблема устранена
  5. Если исправление не помогло

#Симптом и границы применимости

При повторении транзакции PostgreSQL может вернуть SQLSTATE 40001 serialization_failure или 23505 unique_violation. В отличие от взаимной блокировки это часто связано с повторным применением бизнес-операции либо уровнем изоляции; корректная стратегия учитывает уже выполненные внешние действия.

#Как отличить возможные причины

Возможная причинаКак узнатьЧто делать
Конфликт сериализацииSERIALIZABLE обнаруживает историю, которую нельзя упорядочить.SQLSTATE 40001Повторять всю транзакцию
Повтор платежа/заказаКлиент не уверен, прошёл ли COMMIT, и вставляет дубль.23505 при повторе того же запросаИдемпотентный ключ
Неполный повторКод повторяет только последний UPDATE, а не всю логику чтения.Ошибка снова возникает при прежних вычисленияхПовторить последовательность целиком

#Пошаговая диагностика и безопасное исправление

  1. 1. Различите SQLSTATE, а не общий текстСохраните код ошибки и серверный CONTEXT в журнале; 40001 отличается от 40P01 и 23505. Для 40001 PostgreSQL ожидает повтор всей транзакции, для уникальности — анализ ключа.
  1. 2. Проверьте выставленный уровень изоляцииВ psql из тестового соединения прочитайте настройку по умолчанию, отдельно проверьте явное BEGIN ISOLATION LEVEL в коде. Не снижайте SERIALIZABLE до READ COMMITTED лишь ради исчезновения ошибки.
  2. psql; чтение
    SQLКоманда
    psql; чтение. Текущий уровень внутри активной транзакции может быть установлен приложением отдельно.
    SHOW default_transaction_isolation;
  1. 3. Выделите полную транзакцию в приложенииОтметьте в коде начало транзакции, все SELECT, вычисления, запись, COMMIT и внешние вызовы. Повтор с середины способен использовать устаревшие данные, а повтор почты или платежа создать дубли.
  1. 4. Добавьте ограниченное повторение 40001На тестовой среде разработчик должен перехватывать именно SQLSTATE 40001, выполнять ROLLBACK, создавать новую транзакцию и повторять все чтения и записи с ограничением числа попыток и случайной задержкой. Бесконечный цикл запрещён.
  1. 5. Защитите бизнес-операцию от повторной доставкиДля заказов/платежей используйте постоянный идемпотентный ключ и UNIQUE в схеме, а внешние побочные действия выполняйте через согласованный механизм подтверждения/очереди. Не передавайте внешний платёж второй раз после спорного COMMIT.
  1. 6. Испытайте параллельное выполнениеНа копии базы запустите два конкурирующих тестовых запроса к одной бизнес-сущности; зафиксируйте попытки и число созданных строк. Успех — итог соответствует одному бизнес-действию, даже если одна попытка получила 40001.
  1. 7. Отделите непредвиденные ошибки23505 после повторного запроса с тем же ключом может означать уже успешный первый запрос: запросите существующую запись и сравните параметры. Все прочие SQLSTATE передавайте как ошибки, а не подавляйте общим except.

#Как проверить, что проблема устранена

После конкурентного запуска транзакции либо успешно завершены по правилам, либо безопасно повторены; один бизнес-запрос не производит два заказа или платежа.

#Если исправление не помогло

Если конфликт сохраняется после нескольких попыток, исследуйте порядок чтения/блокировок и долгие транзакции. Не расширяйте число повторов без анализа нагрузки.

Так эту ошибку тоже называют

Источники и обновления

Последнее обновление статьи: 2 ноября 2026. Версии программ и интерфейсы меняются — если шаги не совпадают с вашей версией, сверяйтесь с документацией производителя.