{ "index": 184, "slug": "editorial-2022-11-field-bitrix-performance", "title": "Когда тормозит Bitrix-страница: как найти границу проблемы и не сломать кеш", "excerpt": "Практический разбор медленной Bitrix-страницы: отделяем запрос, компонент, кеш и шаблон, проверяем ключи результата и принимаем только обратимые решения.", "contentHtml": "
Пользователь открывает каталог, ждёт дольше обычного и обновляет страницу. В чате появляется короткий диагноз: «тормозит Bitrix». После него разработчик уменьшает JavaScript, администратор очищает весь кеш, а владелец сервиса просит увеличить сервер. Страница может не измениться, зато команда теряет исходное состояние. Нельзя понять, что проверяли, какой слой дал задержку и что безопасно вернуть. Цена ошибки — лишняя нагрузка, повторная работа и риск показать одному посетителю результат другого.
\nТезис простой: производительность Bitrix-страницы нужно разбирать по границам, а не по названию платформы. Сначала фиксируем один маршрут и его входы. Затем разделяем компонент, его кеш и шаблон. Только после этого выбираем небольшой diff и критерий проверки. Если вход кеша неизвестен, правильное действие — остановиться и уточнить контракт, а не отключать кеш наугад.
\nУ страницы есть несколько последовательных слоёв. HTTP-запрос выбирает маршрут и параметры. Компонент получает эти параметры и строит данные. Встроенное кеширование решает, можно ли вернуть сохранённый результат или нужно выполнить код заново. Шаблон превращает результат компонента в HTML. Браузер получает уже собранный ответ и отдельно тратит время на его разбор, стили и скрипты.
\nЭти слои связаны, но не доказывают друг друга. Имя шаблона не показывает, какой SQL выполнился. Большой HTML не доказывает, что база медленная. Настройка кеширования не доказывает, что конкретный запрос получил cache hit. В Bitrix метод StartResultCache возвращает false, когда действующий результат можно вывести, и true, когда компонент должен сформировать результат. Это контракт ветвления, а не замер времени страницы.
Ключ кеша должен учитывать каждый вход, который меняет HTML. Если результат зависит от сайта, компонента, шаблона, параметров и сегмента посетителя, эти различия нельзя скрыть в коде шаблона. Иначе две разные страницы могут использовать один сохранённый результат. Если часть результата нужна после чтения кеша, компонент может передать выбранные ключи через SetResultCacheKeys. Это управляет составом данных, доступных после кеширования; оно не ускоряет произвольный SQL и не исправляет неверный ключ.
Ниже — ограниченный пример. Он не запускает Bitrix, не обращается к базе и не измеряет ответ. Модель только проверяет, что перед изменением шаблона назван полный набор входов кеша. В реальном проекте список нужно подтвердить по компоненту, параметрам и условиям, которые действительно меняют HTML.
\n<?php\n$requiredKeyParts = [\n 'SITE_ID',\n 'component',\n 'template',\n 'arParams',\n 'visitor-segment',\n];\n\n$declaredKeyParts = [\n 'SITE_ID',\n 'component',\n 'template',\n 'arParams',\n];\n\n$missing = array_values(array_diff($requiredKeyParts, $declaredKeyParts));\n\nif ($missing !== []) {\n throw new RuntimeException(\n 'change-blocked: missing cache input ' . implode(', ', $missing)\n );\n}\n\n$rollbackTemplate = 'catalog-grid';\n$nextTemplate = 'catalog-grid-minimal';\nВ этом учебном запуске действие блокируется из-за visitor-segment. Это не означает, что именно сегмент замедляет страницу или что он обязательно должен входить в ключ. Это означает только одно: пока неизвестно, меняет ли он результат и где учтён, менять кеш или шаблон рано. Отрицательная ветка защищает от правки, которая выглядит локальной, но меняет данные для разных вариантов запроса.
Если контракт подтверждён, шаблон можно менять как отдельный обратимый diff. В описании сохраняют старый владелец шаблона, новый владелец и условие возврата. После изменения повторяют тот же вход. Сравнение другого URL, другой роли или очищенного кеша не отвечает на исходный вопрос.
\n| Симптом | Вероятная причина | Проверка | Действие |
|---|---|---|---|
| Каталог медленный на одном URL | Смешаны маршрут и общий разговор о платформе | Зафиксировать URL, параметры, роль и вариант страницы | Повторить один и тот же вход |
| Очистка кеша временно меняет поведение | Изменили состояние, но не нашли зависимость | Сверить ветку кеша и полный список входов | Не очищать весь кеш как доказательство |
| В ключе нет внешнего признака | HTML зависит от данных, которых нет в контракте | Проверить, меняет ли признак результат компонента | Остановить diff и назвать недостающий вход |
| Виноватым объявили шаблон | Видимый файл приняли за источник задержки | Отделить сбор данных от рендера HTML | Собрать артефакт именно на нужной границе |
| После оптимизации цифра изменилась | Сравнили разные условия или разные кеш-состояния | Повторить исходный сценарий и способ измерения | Оставить только подтверждённый diff |
| Нет доступа к профилю или логу | Гипотезу пытаются выдать за факт | Записать ограничение и владельца следующего шага | Не утверждать причину без артефакта |
Начните с точки подключения компонента и его параметров. Запишите имя компонента, имя шаблона, время кеширования, режим обновления и все условия, которые меняют данные или HTML. Проверьте, не добавляет ли шаблон зависимость от авторизации, группы пользователя, языка, региона, cookie или внешнего сегмента. Каждый такой признак должен иметь понятное место в контракте. Если он не влияет на результат, это тоже нужно обосновать.
\nЗатем отделите две задачи. Первая — решить, можно ли повторно использовать результат. Вторая — определить, какие данные должны быть доступны при выводе этого результата. SetResultCacheKeys относится ко второй задаче. Нельзя применять его как универсальную настройку производительности. Если компонент не кешируется или ключ строится неполно, список полей не устранит повторные вычисления.
Режим «не кешировать» полезен для изоляции гипотезы только при контролируемом тесте и с понятным ограничением. На рабочем трафике он может увеличить число обращений к базе и время выполнения компонента. Ручная очистка кеша также не объясняет причину: она лишь переводит компонент в другую ветку на следующем запросе. После любого такого эксперимента верните исходный режим и зафиксируйте, что именно изменилось.
\nЭта схема не заменяет профилирование. Без реального запроса нельзя назвать тяжёлый SQL. Без лога нельзя утверждать длительность PHP. Без повторяемого браузерного сценария нельзя объяснить задержку загрузки скриптов. Документация Bitrix описывает API и режимы платформы, но не знает самописный компонент, его интеграции и данные конкретного сайта.
\nОстановитесь, если один и тот же URL получает разный HTML по неописанному условию, если компонент меняет состояние вне своего контракта, если эксперимент запрещён на рабочем трафике или если результат нельзя безопасно откатить. В таком случае полезный итог — не «причина найдена», а точное ограничение: какой факт отсутствует, где его получить и кто отвечает за следующий шаг.
\nУчебный код выше нельзя переносить в production как готовую оптимизацию. В нём нет проверки версии ядра, политики персональных данных, реального состава параметров, схемы инвалидирования и нагрузки. Его роль — показать stop condition. Рабочее решение требует локальной проверки и отдельного плана возврата.
\nДиагностика готова к изменению, если другой инженер без устного объяснения может ответить на пять вопросов: какой вход воспроизводят; какой компонент и шаблон рассматривают; какие данные входят в ключ; какой артефакт подтверждает гипотезу; что вернут при отрицательном результате. После diff можно повторить тот же сценарий, увидеть тот же ожидаемый сигнал и однозначно вернуть предыдущее состояние.
\nЕсли на любой вопрос отвечает «обычно Bitrix делает так», работа не готова. Замените общую фразу конкретным неизвестным: «не установлено, влияет ли группа пользователя на HTML», «не подтверждён владелец шаблона» или «нет разрешённого профиля для этого маршрута». Такая формулировка не обещает ускорение. Она делает следующий шаг проверяемым.
\n