Оптимизация каталога на 200 тысяч товаров в Битрикс: разбор
Каталог на 200 тысяч товаров в Битриксе падает не от размера таблиц, а от бота на страницах фильтра, непрогретого кеша и импорта в час пик. Разбираем цепочку причин и порядок лечения.

Каталог на 200 тысяч товаров в Битриксе чаще всего падает не в день запуска. Он падает, когда по страницам фильтра проходит бот, уходит рассылка или ночной импорт из 1С затягивается до утреннего трафика. Сайт отвечает 502 и 504, а MySQL занят одинаковыми тяжёлыми выборками. Это обобщённый сценарий, а не отчёт по одному проекту: замеров с чужого сервера здесь нет, вместо них описано, что и как снять самому.
Как выглядит инцидент
Рассчитано на «1С-Битрикс: Управление сайтом» с модулями инфоблоков и торгового каталога, MySQL 8.0 или MariaDB 10.x, Nginx и PHP-FPM. В вашем релизе названия настроек могут отличаться.
Первые минуты выглядят так:
- пул PHP-FPM выбран целиком (упёрлись в
pm.max_children), в логе Nginx растёт доля 502 и 504; - в списке процессов MySQL висят однотипные запросы к таблицам свойств инфоблока, и очередь не рассасывается;
- в access-логе большинство запросов - страницы разделов с параметрами фильтра, нередко с одним User-Agent;
- в
/bitrix/cacheфайлов становится всё больше, а переиспользуются они всё реже.
Механизм: почему падает именно каталог
Свойства как строки
В хранении свойств версии 1 значения лежат в b_iblock_element_property: строка на каждое значение каждого свойства каждого товара. Заполните 30 свойств у 200 тысяч товаров, и это порядка 6 миллионов строк. Фильтр по трём свойствам соединяет такую таблицу саму с собой трижды. В версии 2 одиночные свойства лежат в b_iblock_element_prop_s с номером инфоблока, по строке на товар, а множественные в b_iblock_element_prop_m. Торговые предложения хранятся в отдельном инфоблоке, так что с ними строк ещё больше.
Умный фильтр без фасетного индекса
Компонент bitrix:catalog.smart.filter не только фильтрует, но и считает, какие значения ещё доступны в текущей выборке. Без фасетного индекса (пояснение в конце) это тяжёлые запросы на каждое открытие страницы. Индекс заранее раскладывает значения под такой подсчёт. Цена: его нужно построить, на большом каталоге это долго (время мерьте на копии), а массовые правки товаров становятся медленнее.
Кеш не успевает прогреться
Ключ кеша компонента включает параметры фильтра, и при CACHE_FILTER = Y каждая комбинация хранится отдельным файлом. Десять свойств по пять значений дают 610, то есть больше 60 миллионов возможных адресов (шесть вариантов на свойство с учётом «не выбрано»). Бот обходит ссылки быстрее, чем комбинации повторяются, и каждый промах уходит в базу. Поставите CACHE_FILTER = N, и в базу уйдёт любой запрос с фильтром.
Каталог редко падает от размера таблиц. Он падает, когда число разных запросов растёт быстрее, чем кеш успевает их запомнить.
Порядок лечения
Сначала дешёвое и обратимое, затем то, что трогает данные. Перед шагами 3 и 4 нужны свежая копия базы и прогон на копии.
- Снять картину. Включите slow log и посмотрите размеры таблиц (запросы ниже). Модуль perfmon покажет тяжёлые запросы со стороны приложения.
- Убрать лишний трафик. Закройте адреса с результатами фильтра от обхода через robots.txt, noindex или canonical (шаблон зависит от того, включены ли ЧПУ умного фильтра). Ограничьте частоту запросов к каталогу в Nginx директивами
limit_req_zoneиlimit_req. - Включить фасетный индекс в окно низкой нагрузки. Опция лежит в настройках модуля инфоблоков, после смены набора свойств фильтра индекс пересоздают. Проверьте по slow log, что запросы подсчёта значений ушли.
- Проверить версию хранения свойств у инфоблоков каталога и предложений. Если стоит первая, планируйте преобразование во вторую в окно обслуживания: операция массовая, откат только из копии.
- Развести фоновую работу. Агенты перенесите на cron, импорт из 1С и переиндексацию поиска уберите из часа пик.
- Вынести кеш с диска в memcached или Redis по документации вашей версии. Нагрузку на файловую систему это снимет, а рост числа комбинаций нет.
Запросы для первого шага. Для SET GLOBAL нужны административные права, значения сбрасываются после перезапуска сервера, а table_rows у InnoDB - оценка, не точный счёт.
-- Самые тяжёлые таблицы инфоблоков (MySQL 8.0 / MariaDB 10.x)
SELECT table_name,
table_rows,
ROUND((data_length + index_length) / 1024 / 1024) AS size_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name LIKE 'b\_iblock%'
ORDER BY (data_length + index_length) DESC
LIMIT 15;
-- Журнал медленных запросов: всё, что дольше 1 секунды
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;Что мерить до и после
Фиксируйте условия: один и тот же набор URL, холодный и тёплый кеш отдельно, одинаковые окна времени. Время построения индекса и длительность импорта записывайте на одной и той же выгрузке.
| Показатель | Чем снять | Что записать |
|---|---|---|
| Время ответа раздела с фильтром | $request_time в access-логе Nginx | p50 и p95 по одному набору URL |
| Топ запросов по суммарному времени | slow log и pt-query-digest из Percona Toolkit | До и после, за равные окна |
Размер и число файлов в /bitrix/cache | du и find | Значение раз в сутки |
| Занятость пула PHP-FPM | страница статуса (pm.status_path) | Максимум в пике |
Где подход ломается
- Фасетный индекс не узнает об изменениях, сделанных прямыми SQL-запросами в обход API Битрикса: фильтр начнёт показывать устаревшие значения.
- Закрытие фильтра от ботов бьёт по SEO, если вы продвигаете страницы вроде «бренд плюс категория». Закрывайте выборочно.
- Ограничение по IP в Nginx задевает покупателей за общим NAT и поисковых ботов. Подлинность ботов проверяйте по документации поисковиков, пороги подбирайте по access-логу.
- После перезапуска memcached или Redis кеш пуст, и вся нагрузка разом уходит в базу. Перезапускайте вне пика.
Что делать дальше
Первым делом на неделю включите slow log и access-лог с $request_time, не меняя больше ничего: без базовой линии не докажете, что правки помогли. Затем заведите алерт на занятость пула PHP-FPM. Настройка MySQL, например innodb_buffer_pool_size, - отдельная тема, здесь она не разбирается.
Фасетный индекс. Служебные таблицы, в которых заранее хранится, какие товары имеют какие значения свойств. По ним умный фильтр считает количество и доступные значения без многократного обхода таблиц свойств. Индекс обновляется при изменении товаров через API Битрикса.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Документация для разработчиков 1С-Битрикс, MySQL 8.0 Reference Manual: The Slow Query Log
Частые вопросы
А если у меня другая версия Битрикса или MariaDB вместо MySQL?
Фасетный индекс включён, а фильтр всё равно медленный. Что проверить?
Можно ли включать фасетный индекс и переводить свойства на хранение 2.0 на работающем проде?
Как понять, что виноваты боты, а не живые покупатели?
Хватит ли просто взять сервер мощнее?
Что делать, если после закрытия фильтра от индексации просели позиции?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.