15k+ заказов в месяц — не рекорд, но уже тот уровень, на котором стандартная конфигурация 1С-Битрикс начинает сыпаться на пиках: распродажи, рассылки, боты индексации, набежавшие одновременно. Две вещи снимают основную нагрузку с PHP и MySQL: правильно настроенный композитный кеш и индексы под реальные запросы каталога. Настройка PHP-FPM, OPcache и Redis под сессии — отдельная тема, здесь не про неё. Дальше — почему именно кеш и индексы, а не что-то экзотическое.
Композитный кеш кеширует не всю нагрузку
Композит делит страницу на две части: статичную "оболочку" — HTML, который на первом запросе к URL сохраняется на диск и дальше отдаётся сразу, — и динамические зоны (корзина, данные пользователя, персональные блоки), которые разработчик помечает отдельно в компонентах. Динамические зоны подгружаются отдельным AJAX-запросом уже после того, как статика показана в браузере.
Это работает не только для анонимных посетителей — композит можно включить и для авторизованных групп пользователей, если правильно настроить список групп в настройках (важно не включить туда группы с доступом к административной панели — частая ошибка конфигурации). Но чем персонализированнее страница, тем больше в ней динамических зон и тем меньше реальный выигрыш от статичного кеша — часть работы всё равно уходит в отдельный запрос при каждом показе.
Второе больное место — момент инвалидации. Сбрасывается кеш не по конкретному URL, а по тегам: у каждой закешированной
страницы есть привязанные теги (например, iblock_id_N для инфоблока), и при изменении данных Битрикс сбрасывает
все страницы с этим тегом разом. Если тег расставлен грубо — на весь инфоблок целиком — смена цены одного товара
сбрасывает кеш всего каталога, а не одной карточки. Файл не пересоздаётся сам по себе — он генерируется заново
на первый же запрос к сброшенной странице: этот запрос идёт в PHP и MySQL напрямую, дальше страница снова отдаётся из файла.
Главный выигрыш композита — не в том, что PHP отдаёт готовый HTML быстрее, а в том, что PHP может не запускаться вообще.
Готовая страница физически лежит файлом в bitrix/html_pages/, и при настроенном nginx (try_files на этот путь) файл
отдаётся веб-сервером напрямую, минуя PHP-FPM целиком. Без этой настройки композит просто ускоряет PHP-путь, а не убирает его.
Что с этим реально делать:
- проверить, настроена ли у nginx отдача из
bitrix/html_pages/напрямую — без этого композит работает, но не даёт главного эффекта; - не считать композит защитой от нагрузки "по умолчанию для всех" — то, для каких групп он реально включён, нужно проверять в настройках, а не предполагать;
- на страницах с большим количеством персонального контента (личный кабинет) выигрыш от композита ограничен — там динамических зон больше, чем статики;
- расставлять теги кеша по смыслу (на элемент, не на весь инфоблок) — это доработка компонента/шаблона, не галочка в настройках, но именно она не даёт сбросу одной цены чистить весь каталог разом.
Индексы под каталог — то, что реально экономит время БД
1С-Битрикс хранит товары и их свойства по модели "элемент + значения свойств" (EAV) — гибко для админки, но каждое дополнительное свойство в фильтре превращается в лишний JOIN. На каталоге в несколько тысяч позиций это не заметно. На 100k+ позиций и фильтре по 3-4 свойствам разница между "есть индекс под нужную комбинацию колонок" и "индекса нет" — это разница между запросом на десятки миллисекунд и запросом на секунды под нагрузкой.
Для фильтра по свойствам (умный фильтр в каталоге) в Битриксе есть штатный механизм — фасетный индекс (Контент → Инфоблоки → Фасетные индексы). Он один раз собирает свойства элементов в отдельную плоскую таблицу и дальше фильтрует по ней, а не по цепочке JOIN'ов через EAV-таблицы. Для стандартного фильтра каталога это первое, что нужно включить, а не писать вручную индексы под таблицы свойств — MySQL такой ручной индекс использует как любой другой, но пересборка фасета может таблицу свойств пересоздать и снести его без предупреждения.
Ручные индексы MySQL нужны там, где штатный фасетный индекс не участвует — под конкретный медленный запрос, который
не покрывается умным фильтром (нестандартная сортировка, кастомная выборка в компоненте и т.п.). Готового универсального
примера тут нет и не должно быть — какой индекс нужен, зависит от конкретного запроса и от того, какие индексы на
таблице уже есть. Смотреть по факту: включить perfmon, найти в логе медленных запросов конкретный SQL, прогнать через
EXPLAIN и добавлять индекс под то, что реально показал план выполнения — не заранее и не по аналогии с чужим примером.
Практическое правило: индекс должен соответствовать не структуре таблицы, а конкретному запросу, который реально выполняется в фильтре и сортировке каталога.
Итог
Ни композитный кеш, ни индексы сами по себе не "решают" нагрузку целиком — они снимают конкретную её часть: композит — повторный полный рендер статичной части страницы, индексы — полное сканирование таблиц под фильтрами каталога. Остальное (личный контент на странице, момент инвалидации кеша, редкие тяжёлые запросы) остаётся и требует отдельного внимания — про это дальше отдельными постами.
