Yandex Metrika
sanches.free 20 просмотров

Дедлок MySQL в модуле Sale: корзина и связанные товары

Откуда берётся deadlock в интернет-магазине

В MySQL InnoDB дедлок — это не «ошибка логики запроса», а фиксация цикла ожиданий блокировок между транзакциями. Пока клиенты оформляют заказы, фоновые задачи пересчитывают скидки или статистику покупок вместе с корзиной (b_sale_basket), несколько сессий могут одновременно трогать одни и те же агрегирующие строки. Если порядок захвата строк отличается, СУБД выбирает одну из транзакций как «жертву» и откатывает её с сообщением о deadlock.

Что за таблицы в цепочке

b_sale_basket хранит позиции корзины и привязку к заказу. Таблица b_sale_product2product обычно используется для учёта совместных покупок (связь «товар — сопутствующий») и накопительных счётчиков вроде CNT. Массовый UPDATE, который соединяет корзину с самой собой и одновременно обновляет счётчики по парам PRODUCT_ID / PARENT_PRODUCT_ID, легко создаёт широкий набор затронутых строк и долгие блокировки.

Фрагмент реального лога

Ниже — сокращённый вывод из трассировки InnoDB: две транзакции выполняют одинаковый по структуре запрос, но с разными ORDER_ID. Именно пересечение по строкам b_sale_product2product при разном порядке их захвата и приводит к взаимной блокировке.

*** (1) TRANSACTION:
UPDATE b_sale_product2product p2p, b_sale_basket b, b_sale_basket b1
  SET p2p.CNT = p2p.CNT + 1
  WHERE b.ORDER_ID = b1.ORDER_ID
    AND b.ID <> b1.ID
    AND b.ORDER_ID = 33989
    AND p2p.PRODUCT_ID = b.PRODUCT_ID
    AND p2p.PARENT_PRODUCT_ID = b1.PRODUCT_ID;

*** (2) TRANSACTION:
UPDATE b_sale_product2product p2p, b_sale_basket b, b_sale_basket b1
  SET p2p.CNT = p2p.CNT + 1
  WHERE b.ORDER_ID = b1.ORDER_ID
    AND b.ID <> b1.ID
    AND b.ORDER_ID = 33990
    AND p2p.PRODUCT_ID = b.PRODUCT_ID
    AND p2p.PARENT_PRODUCT_ID = b1.PRODUCT_ID;

Как действовать после инцидента

  • Снимите полный дамп статуса движка для конкретного момента: в рабочей среде это делают через стандартные средства мониторинга InnoDB и журналы ошибок MySQL — по ним видно последовательность ожиданий и индексы, по которым навешивались блокировки.
  • Проверьте, нет ли дублирующихся агентов или параллельного запуска одного и того же пересчёта для пересекающихся заказов; иногда достаточно сериализовать блок обновления статистики по ключу склада или витрины.
  • Убедитесь, что запрос использует предсказуемый доступ к строкам (одинаковый ORDER BY внутри критической секции, индексы под реальные условия WHERE), чтобы снизить вероятность «перекрёстного» порядка блокировок.
  • На уровне приложения допустимы повтор запроса после ответа о deadlock и снижение длины транзакций: не держите открытую транзакцию на время внешних HTTP-вызовов и тяжёлых циклов.

Итог

Такие дедлоки — сигнал, что конкурирующие потоки массово обновляют связанные счётчики одной и той же таблицы. Разбор лога показывает конкретные идентификаторы заказов и текст запроса; дальше работа сводится к уменьшению пересечения транзакций и выравниванию порядка доступа к строкам, а не к «отключению» проверок со стороны СУБД.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии