MySQL и MariaDB LinuxWindowsсервер MySQL

MySQL: Приложение создаёт дубли записей при повторной попытке транзакции

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

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

Повтор одного HTTP-запроса, задания очереди или транзакции после тайм-аута может создать две одинаковые бизнес-записи. Сетевая ошибка не доказывает, что первая транзакция откатилась; нужна идемпотентная операция с уникальным ключом.

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

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

Повтор одного HTTP-запроса, задания очереди или транзакции после тайм-аута может создать две одинаковые бизнес-записи. Сетевая ошибка не доказывает, что первая транзакция откатилась; нужна идемпотентная операция с уникальным ключом.

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

Возможная причинаКак узнатьЧто делать
Повтор после потери ответаСервер сохранил запись, но клиент не получил подтверждение.Две строки с одним номером заказаСверить журнал с идентификатором операции
Проверка без уникальностиПриложение сначала делает SELECT, затем INSERT, а параллельные процессы оба не находят строку.Дубли только при конкурентной нагрузкеДобавить бизнес-ограничение UNIQUE после очистки
Разные ключи повтораДля каждой повторной попытки генерируется новый идентификатор операции.Повторы имеют разные request_idПередавать один ключ для всей операции

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

  1. 1. Определите, что именно дублируетсяВ панели приложения найдите два одинаковых заказа или события и выпишите пользовательский ключ, время и ID запроса из журнала. Проверьте, не являются ли записи двумя законными покупками: удаление одной без проверки может нарушить учёт.
  1. 2. Проверьте, что означает сообщение о сбоеСравните момент выполнения INSERT с тайм-аутом клиента и записью COMMIT в журнале приложения. Если ответ потерялся после фиксации, повтор должен вернуть ранее созданный результат, а не делать ещё одну вставку.
  1. 3. Изучите действующие уникальные индексыВ mysql-клиенте запросите описание индексов вашей таблицы. Если уникален только технический id, он не мешает создать два заказа с одинаковым внешним идентификатором.
  2. Чтение; appdb.orders — пример
    SQLКоманда
    Чтение; appdb.orders — пример. Не создавайте индексы без проверки имеющихся дублей.
    SHOW INDEX FROM appdb.orders;
  1. 4. Найдите повторы по бизнес-ключуНа копии базы выполните группировку по idempotency_key или другому реальному бизнес-идентификатору. Не используйте GROUP BY по имени покупателя: разные покупки могут иметь одинаковое имя.
  2. Чтение; idempotency_key — пример поля, которое должно сохраняться при всех повторах
    SQLКоманда
    Чтение; idempotency_key — пример поля, которое должно сохраняться при всех повторах.
    SELECT idempotency_key, COUNT(*) AS n FROM appdb.orders GROUP BY idempotency_key HAVING COUNT(*) > 1 LIMIT 20;
  1. 5. Обеспечьте устойчивый ключ операцииНа уровне HTTP/очереди создавайте уникальный ключ один раз при начале операции и передавайте его неизменным при повторном запросе. В структуре тестовой базы после устранения исторических дублей установите UNIQUE на этот ключ. Перед DDL проверьте блокировки и резервную копию.
  1. 6. Применяйте атомарную вставку с повторным чтениемВ тестовой среде разработчик должен использовать уникальный индекс вместе с INSERT ... ON DUPLICATE KEY UPDATE либо перехватом ошибки уникальности и запросом уже существующей строки. Обновлять сумму заказа в ветке конфликта без бизнес-логики нельзя.
  1. 7. Смоделируйте потерю ответа в тестеОтправьте одну операцию и дважды повторите её с тем же ключом, имитируя тайм-аут после фиксации. Ожидается один заказ с одним ID; проверьте журнал клиента, сумму и побочные действия вроде оплаты и отправки письма.
  1. 8. Верните изменения только после успешной проверкиПри появлении двух записей на тесте вернитесь к проверке ключа и границ транзакции; не включайте повторную отправку на рабочих платежах. Переносите исправление поэтапно с наблюдением за числом конфликтов.

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

Повторные запросы с одним ключом возвращают одну и ту же запись, а уникальное ограничение не допускает вторую строку; побочные действия также не повторяются.

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

Проверьте, не находится ли запись платежа или событие внешней системы вне транзакции базы: тогда одной уникальности недостаточно, потребуется согласованный механизм повторной обработки.

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

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

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