Оформление заказа в магазине на Битрикс

При оформлении заказа наблюдалась сильная задержка, особенно при количестве товаров в корзине более 50.

Поиск проблемы с помощью профилировщика xhprof

Максимальная найденная задержка - 700 сек. Для предотвращения ситуации прерывания скрипта выставлено max_execution_time в 900 сек и отменено прерывание скрипта при потере соединения с клиентом ignore_user_abort(true);.

Поиск медленных SQL запросов с помощью Percona

Percona выдала следующую картину профилирования медленных запросов:

# Rank Query ID           Response time Calls R/Call  V/M   Item
# ==== ================== ============= ===== ======= ===== ==============
#    1 0x93CFB226660D35EA 50.0813 76.3%     1 50.0813  0.00 UPDATE b_sale_product?product b_sale_basket
#    2 0xB40B7BB93898D8B5 11.3354 17.3%     1 11.3354  0.00 INSERT SELECT b_sale_product?product b_sale_basket b_sale_product?product
#    3 0x5D460ECCCA8798B9  2.2000  3.4%     1  2.2000  0.00 SELECT b_iblock b_lang b_iblock_element
# MISC 0xMISC              1.9989  3.0%    28  0.0714   0.0 <24 ITEMS>

Самый медленный запрос:

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 = 36057 AND
          p2p.PRODUCT_ID = b.PRODUCT_ID AND
          p2p.PARENT_PRODUCT_ID = b1.PRODUCT_ID\G

Вначале был сделан индекс, что сократило время на тестовом наборе с 10 до 3 сек:

ALTER TABLE `b_sale_basket` ADD INDEX `idx_save_order` (`ORDER_ID`, `ID`, `PRODUCT_ID`); -- 10 сек => 3 сек

Анализ и варианты решения

Таблица b_sale_product2product отвечает за функционал «с этим товаром также покупают», причем несмотря на периодическую чистку ее размер вырос до 4,5 млн записей!

Решение - отключить функционал «С этим товаром покупают», так как он не используется. Варианты:

  • штатными средствами: Настройки продукта > Настройки модулей > Интернет-магазин > Настройки также продаваемых продуктов. Возможно, что снятие всех опций поможет =)
  • исправить код - патч может слететь при обновлении (return; в начале addProductsFromOrder и deleteOldProducts)
  • сделать «черную дыру» ALTER TABLE b_sale_product2product ENGINE = BLACKHOLE - оригинальный способ
  • удалить событие onSaleOrderAdd

Удалить событие Битрикса onSaleOrderAdd из админки нельзя, но можно это сделать запросом:

DELETE FROM `b_module_to_module` WHERE `TO_CLASS` = '\\Bitrix\\Sale\\Product2ProductTable' AND `TO_METHOD` = 'onSaleOrderAdd';

В итоге среднее время сократилось с 96 до 0,6 сек.

В последствии выяснилось, что еще надо вырубить агент CSaleProduct::RefreshProductList();:

Печать/экспорт