Итак, симптомы следующие: долгое (60 сек и выше) сохранение элемента/товара через админскую форму редактирования. При импорте и выполнении из консоли проблем не наблюдается. Клиент недоволен, техподдержка в панике. Анализ на стороне хостера выявляет долгий запрос вида:
UPD ATE b_iblock_element SE T TIMESTAMP_X = TIMESTAMP_X, SHOW_COUNTER_START = ifnull(SHOW_COUNTER_START, now()), SHOW_COUNTER = ifnull(SHOW_COUNTER, 0) + 1 WH ERE ID=ИД_сохраняемого_элемента |
[spoiler]
Беглый анализ позволяет установить, что тормозящий запрос - это вызов метода ClBlockElement::CounterInc. Метод вызывается для увеличения счетчика просмотров в публичных компонентах и только. Мистика.
Продолжаем поиск - открываем код страницы редактирования и видим, что сохранение элемента с незапямятных времен работает через транзакции. Делается это, чтобы откатить все изменения, если на каком-то этапе сохранения произошла ошибка.
Берем все вызовы методов (CIBlockElement::Update, обновление полей цен и товара, работу с документооборотом), находящиеся внутри блока транзакции. Последовательно проверяем - есть ли на проекте обработчики событий, что вызываются в этих методах. Если результат отрицательный - разбираем методы детально и смотрим уже их (например, для метода CIBlockElement::Update таким является вызов CIBlockElement::UpdateSearch, а в нем - CSearch::Index). Процесс продолжаем рекурсивно. Учтите, в проекте могут быть и другие обработчики, а в них - тоже вызовы api. В итоге - бинго! В одном из обработчиков находим http-запрос к публичной детальной странице элемента. А там в компоненте включено обновление счетчика просмотров. Зачем нужен обработчик - это отдельный разговор. Отключаем его - все тормоза исчезают. Как говорил герой известного фильма - что это было?
При открытии транзакции строка (а в худшем случае - вся таблица) блокируется от записи. Это, кстати, одна из причин, почему использование транзакций запрещено в методах api. Все попытки изменения записи, кроме текущей, ставятся в очередь ожидания на уровне базы. Пока транзакция не закрыта или не отменена - очередь ждет.
И тут возникает наш обработчик со своим запросом. Ну и что, скажете вы? Ничего, если бы счетчик просмотров был отключен. Но тогда не было бы проблемы.
Как выглядит ожидаемый план сохранения данных:
1. Открываем транзакцию 2. Обновляем строку в b_iblock_element 3. Запись блокируется от внешнего изменения (другой хит) на уровне БД 4. Закрываем транзакцию 5. Запись разблокируется 6. Выполняется очередь изменений с других хитов (если успела накопиться) |
1. Открываем транзакцию 2. Обновляем строку в b_iblock_element 3. Запись блокируется от внешнего изменения (другой хит) на уровне БД Внезапно 4. Вызывается обработчик 5. Идет хит на публичную страницу 6. Пытаемся обновить счетчик просмотров (та же самая строка в b_iblock_element) 7. Ждем (хит-то другой) 8. Ждем 9. Еще ждем 10. Устали уже ждать .... N-й шаг. Устали ждать, отвалились по таймауту. Обработчик завершен. N+1. Если время исполнения скрипта не вышло - закрываем транзакцию. Вышло - просто падаем. N+2. Запись разблокируется N+3. И тут, наконец, выполнится увеличение счетчика просмотров |
Причины реализации подобных обработчиков могут быть разные. Тем не менее, в идеале на проекте их быть не должно. Это основная и главная рекомендация.
Однако, бывают ситуации... Что можно в таких случаях сделать:
1. Прямое обращение к публичной странице заменяется на добавление в очередь обработки (своя таблица) и агента/скрипт на кроне, который побежит по очереди и выполнит требуемое (обращение к странице и какие-то действия с результатом). Плюс - все достаточно кошерно. Минус - квалификация не ниже средней.
2. если п.2 невозможен - передавать в строке обращения некий параметр (скажем disableCounter=Y). На самой странице параметр вызова компонента USE_ELEMENT_COUNTER (на примере bitrix:catalog или catalog.element) меняем так:
было
"USE_ELEMENT_COUNTER" => "Y" |
"USE_ELEMENT_COUNTER" => (isset($_REQUEST['disableCounter']) && $_REQUEST['disableCounter'] === 'Y') ? 'N' : 'Y' |
Это все, что я имею сказать по данной проблеме. Желаю вам быстрых запросов и отклика сайта.
У нас возникла похожая проблема, после внесения изменений в карточку товара и нажатия сохранить.
Карточка сохраняется 20-22 секунд (навигация по разделам, тоже где-то 8-10 секунд).
Интернет-магазин, 20000 товаров.
Созданных свойств 2100.
В гугл панели показывает, что 19 секунд Waiting (TTFB) - время до получения первого байта (сетевого пакета) веб-страницы после отправки запроса со стороны клиента.
Что было сделано:
1. Переехали на более производ.тариф хостинга 3проца, 6 гигов
2. Перевели все таблицы на InnoDb
3. Оптимизацию таблиц сделали, обновили фасетный индекс, сгруппировали все 2100 свойств по группам.
5. Хостер, Дополнительно с нашей стороны была проведена работа по добавление настроек кэширующего сервера memcached, перенос временных таблиц в оперативную память и создание swap-раздела размером в 2gb.
Итог: ничего не изменилось, не переезд на другой тариф,не настройка Nginx, не оптимизаия памяти.
Поскажите пож-ста, как можно узнать от чего такие проблемы могут быть?
Ранее как-то не замечали, такой проблемы
скриншот