ГлавнаяБлог15k+ заказов в месяц на 1С-Битрикс без падения под нагрузкой

15k+ заказов в месяц на 1С-Битрикс без падения под нагрузкой

Рамиль Юналиев
Рамиль Юналиев
E-Commerce Lead
7 июля 2026 г.
5 мин чтения

15k+ заказов в месяц — не рекорд, но уже тот уровень, на котором стандартная конфигурация 1С-Битрикс начинает сыпаться на пиках: распродажи, рассылки, боты индексации, набежавшие одновременно. Две вещи снимают основную нагрузку с PHP и MySQL: правильно настроенный композитный кеш и индексы под реальные запросы каталога. Настройка PHP-FPM, OPcache и Redis под сессии или объектный кеш — отдельная тема, здесь не про неё. Дальше — почему именно кеш и индексы, а не что-то экзотическое. Остальные механизмы кэша Битрикс — тегированный кэш D7, highload-блоки, хранилище на memcached/redis — разобраны отдельно, в гайде по кэшированию.

Композитный кеш кеширует не всю нагрузку

Что кешируется, а что нет

Композит делит страницу на две части: статичную "оболочку" — HTML, который на первом запросе к URL сохраняется на диск и дальше отдаётся сразу, — и динамические зоны (корзина, данные пользователя, персональные блоки), которые разработчик помечает отдельно в компонентах. Динамические зоны подгружаются отдельным AJAX-запросом уже после того, как статика показана в браузере.

Это работает не только для анонимных посетителей — композит можно включить и для авторизованных групп пользователей, если правильно настроить список групп в настройках (важно не включить туда группы с доступом к административной панели — частая ошибка конфигурации). Но чем персонализированнее страница, тем больше в ней динамических зон и тем меньше реальный выигрыш от статичного кеша — часть работы всё равно уходит в отдельный запрос при каждом показе.

Момент инвалидации

Второе больное место — момент инвалидации. Сбрасывается кеш не по конкретному URL, а по тегам: у каждой закешированной страницы есть привязанные теги (например, iblock_id_N для инфоблока), и при изменении данных Битрикс сбрасывает все страницы с этим тегом разом. Если тег расставлен грубо — на весь инфоблок целиком — смена цены одного товара сбрасывает кеш всего каталога, а не одной карточки. Файл не пересоздаётся сам по себе — он генерируется заново на первый же запрос к сброшенной странице: этот запрос идёт в PHP и MySQL напрямую, дальше страница снова отдаётся из файла.

PHP может не запускаться вообще

Главный выигрыш композита — не в том, что PHP отдаёт готовый HTML быстрее, а в том, что PHP может не запускаться вообще. Готовая страница физически лежит файлом в bitrix/html_pages/, и при настроенной отдаче этого пути веб-сервером напрямую (например, try_files в nginx, аналогичное правило в Apache или готовая конфигурация Bitrix VM) файл отдаётся, минуя PHP-FPM целиком. Без этой настройки композит просто ускоряет PHP-путь, а не убирает его.

Запрос к странице
      ↓
Веб-сервер: есть готовый HTML-файл в bitrix/html_pages/?
      ↓ да                              ↓ нет
Отдать файл напрямую,             Передать запрос в PHP-FPM
PHP не запускается                       ↓
                                   Собрать страницу (PHP + MySQL)
                                          ↓
                                   Сохранить HTML-файл на диск
                                          ↓
                                   Отдать собранную страницу

Что с этим реально делать:

  • проверить, настроена ли у веб-сервера отдача из bitrix/html_pages/ напрямую — без этого композит работает, но не даёт главного эффекта;
  • не считать композит защитой от нагрузки "по умолчанию для всех" — то, для каких групп он реально включён, нужно проверять в настройках, а не предполагать;
  • на страницах с большим количеством персонального контента (личный кабинет, оформление заказа, сравнение товаров, персональные рекомендации) выигрыш от композита ограничен — там динамических зон больше, чем статики;
  • расставлять теги кеша по смыслу (на элемент, не на весь инфоблок) — это доработка компонента/шаблона, не галочка в настройках, но именно она не даёт сбросу одной цены чистить весь каталог разом.

Если кратко: композит убирает обращение к PHP только пока на диске лежит готовый HTML-файл и веб-сервер отдаёт его напрямую — как только кеш сброшен или страница персональная, часть работы возвращается в PHP и MySQL.

Индексы под каталог — то, что реально экономит время БД

Почему EAV дорого при фильтре по нескольким свойствам

1С-Битрикс хранит товары и их свойства по модели "элемент + значения свойств" (EAV) — гибко для админки, но каждое дополнительное свойство в фильтре превращается в лишний JOIN. На каталоге в несколько тысяч позиций это не заметно. На 100k+ позиций и фильтре по 3-4 свойствам разница между "есть индекс под нужную комбинацию колонок" и "индекса нет" — это разница между запросом на десятки миллисекунд и запросом на секунды под нагрузкой.

Фасетный индекс — сначала проверить, потом писать вручную

Для фильтра по свойствам (умный фильтр в каталоге) в Битриксе есть штатный механизм — фасетный индекс (Контент → Инфоблоки → Фасетные индексы). Он один раз собирает свойства элементов в отдельную плоскую таблицу и дальше фильтрует по ней, а не по цепочке JOIN'ов через EAV-таблицы. Для стандартного фильтра каталога это первое, что стоит проверить — построен ли уже фасетный индекс, — а не сразу писать вручную индексы под таблицы свойств: MySQL такой ручной индекс использует как любой другой, и сама пересборка фасетного индекса эти таблицы не трогает — переиндексация работает со своей отдельной служебной таблицей. Риск для ручного индекса в другом месте — при изменении настроек самого свойства (например, при конвертации свойства из одиночного в множественное) Битрикс меняет структуру таблицы значений, и вручную поставленный на неё индекс может быть потерян.

Запрос каталога с фильтром по свойствам
      ↓
Фасетный индекс включён и построен?
      ↓ да                                   ↓ нет
Фильтр по плоской служебной таблице    Фильтр через JOIN
фасета, без EAV-присоединений          по EAV-таблицам свойств
      ↓                                       ↓
Быстро                                 Медленно на 100k+ строк —
                                        смотреть EXPLAIN, добавлять
                                        индекс под конкретный запрос

Ручные индексы — под конкретный запрос, не заранее

Ручные индексы MySQL нужны там, где штатный фасетный индекс не участвует — под конкретный медленный запрос, который не покрывается умным фильтром (нестандартная сортировка, кастомная выборка в компоненте и т.п.). Готового универсального примера тут нет и не должно быть — какой индекс нужен, зависит от конкретного запроса и от того, какие индексы на таблице уже есть. Смотреть по факту: включить perfmon, найти в логе медленных запросов конкретный SQL, прогнать через EXPLAIN и добавлять индекс под то, что реально показал план выполнения — не заранее и не по аналогии с чужим примером. Разбор конкретных случаев — фильтр по нескольким множественным свойствам, диапазон цены вместе с наличием, сортировка с глубокой пагинацией и перенос части данных в HL-блоки — в отдельном посте про индексы под каталог 100k+ товаров.

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

Если кратко: сначала проверяем, включён и построен ли фасетный индекс, затем по логу медленных запросов и EXPLAIN находим, что реально им не покрыто, и только тогда добавляем свой индекс — под конкретный запрос, а не по аналогии.

Итог

Ни композитный кеш, ни индексы сами по себе не "решают" нагрузку целиком — они снимают конкретную её часть: композит — повторный полный рендер статичной части страницы, индексы — полное сканирование таблиц под фильтрами каталога. Остальное (личный контент на странице, момент инвалидации кеша, редкие тяжёлые запросы) остаётся и требует отдельного внимания — про это дальше отдельными постами.