{ "index": 186, "slug": "editorial-2022-11-practice-bitrix-performance", "title": "Производительность Bitrix-страницы: как найти узкое место без оптимизации наугад", "excerpt": "Разделяем маршрут, компонент, кеш и шаблон, сверяем зависимость результата и выбираем одно обратимое действие вместо отключения кеша вслепую.", "contentHtml": "
Страница каталога открывается медленно, а в разговоре звучит одно объяснение: «тормозит Bitrix». Такой диагноз ничего не проверяет. Он не показывает, какой URL воспроизводит симптом, какой компонент формирует блок, где срабатывает кеш и какой шаблон отдаёт HTML. Цена ошибки — лишние запросы к базе, отключённый кеш, переписанный шаблон и тот же медленный экран.
\nНачинайте с наблюдаемого факта. Для одного входа запишите маршрут, компонент, шаблон, параметры и условие, при котором HTML меняется. Если часть сведений неизвестна, оставьте её неизвестной. Это лучше, чем заменить пробел догадкой. Производительность страницы нельзя объяснить одним слоем: запрос, компонент, кеш и рендеринг связаны, но проверяются отдельно.
\nОдна правка должна отвечать на один вопрос. Например: «входит ли группа пользователя в зависимость кеша этого компонента?» Это проверяемый вопрос. «Почему Bitrix медленный?» — нет. Пока вопрос не ограничен, нельзя выбрать ни инструмент, ни безопасное действие.
\nРазделите страницу на четыре границы. Маршрут задаёт вход. Компонент принимает параметры и получает данные. Кеш решает, нужно ли снова выполнять вычисление и сохраняет ли результат. Шаблон превращает результат компонента в HTML. Большой HTML не доказывает медленный SQL. Наличие кеша в настройках не доказывает попадание в кеш на нужном запросе. Видимый шаблон не доказывает, что он стал причиной задержки.
\n| Симптом | Гипотеза | Проверка | Безопасное действие |
|---|---|---|---|
| Один и тот же каталог долго формирует ответ | Дорогая ветка компонента или промах кеша | Повторить URL и записать компонент, параметры и режим кеша | Получить один разрешённый прикладной артефакт, не меняя TTL |
| Разные пользователи видят разный HTML | Группа, право или сегмент не вошли в ключ | Сверить условия вывода с дополнительной зависимостью и параметрами | Добавить зависимость или остановить изменение до уточнения контракта |
| После очистки кеша первый запрос снова тяжёлый | Очистка убрала результат, но не стоимость построения | Сравнить холодный и повторный вход на одном маршруте | Искать стоимость формирования, а не считать очистку исправлением |
| Кеш работает, но HTML избыточен | В результат попадают лишние данные | Проверить структуру arResult и список ключей результата | Сократить результат только после проверки шаблона и владельца данных |
| После изменения исчезли данные | Нарушена зависимость результата или изменён шаблон | Повторить исходный вход и сравнить HTML и условия доступа | Вернуть один diff и разобрать недостающий вход |
Встроенное кеширование Bitrix начинается с StartResultCache. При действующем кеше метод возвращает false, выводит сохранённое содержимое и заполняет $arResult. При недействительном кеше он возвращает true; компонент получает данные, подключает шаблон, а результат сохраняется при вызове IncludeComponentTemplate или ShowComponentTemplate. Это описание ветки компонента, а не замер всей страницы.
Документация называет базовые части зависимости: сайт, имя компонента, имя шаблона и входные параметры $arParams. Дополнительное условие передают через additionalCacheID. В официальном примере в него входят группы пользователя. Если HTML зависит от права, языка или другого значения, это значение должно участвовать в зависимости результата. Иначе один сохранённый HTML может попасть к другому варианту посетителя.
SetResultCacheKeys решает другой вопрос. Метод перечисляет поля $arResult, которые должны быть доступны при использовании встроенного кеша. Он не измеряет SQL, не уменьшает автоматически время рендера и не превращает тяжёлый компонент в быстрый. Его смысл появляется только после проверки того, какие данные нужны шаблону и коду вокруг компонента.
Ниже показана сокращённая схема. Функция loadCatalogItems условна: в проекте её нужно заменить реальным чтением данных. Группа и язык включены в дополнительный идентификатор только потому, что предположительно меняют HTML. Если это не так, их добавление лишь дробит кеш и не даёт полезной защиты.
$groupKey = implode(',', array_map('intval', $USER->GetGroups()));\n$additionalCacheId = $groupKey . '|' . (string) $arParams['LANGUAGE_ID'];\n\nif ($this->StartResultCache(false, $additionalCacheId)) {\n $items = loadCatalogItems((int) $arParams['IBLOCK_ID']);\n\n if ($items === null) {\n // Некорректный или запрещённый вход не сохраняем как обычный результат.\n $this->AbortResultCache();\n return;\n }\n\n $this->arResult = [\n 'SECTION_ID' => (int) $arParams['SECTION_ID'],\n 'ITEM_COUNT' => count($items),\n 'ITEMS' => $items,\n ];\n $this->SetResultCacheKeys(['SECTION_ID', 'ITEM_COUNT']);\n $this->IncludeComponentTemplate();\n}\nЗдесь null означает ошибочный или недопустимый вход, а пустой массив может быть нормальным пустым каталогом. Это различие важно: пустое состояние, которое можно безопасно показать всем посетителям с одинаковым ключом, не нужно искусственно объявлять ошибкой. AbortResultCache применяйте только когда результат действительно нельзя сохранять.
Массив ITEMS нужен шаблону в ветке построения. Поля SECTION_ID и ITEM_COUNT перечислены отдельно, потому что код после компонента или модификатор результата может обращаться именно к ним. Это не универсальный список. Если шаблон зависит от другой части результата, сначала назовите её и проверьте её место в контракте.
Вызов с false в первом аргументе означает, что время кеширования берётся из $arParams['CACHE_TIME']. Ненулевой CACHE_TIME ещё не доказывает попадание в кеш. Для такого вывода нужен отдельный артефакт: профиль, лог или разрешённый замер на том же маршруте и с тем же вариантом входа.
StartResultCache. Сопоставьте их с фактическими условиями вывода.AbortResultCache.Очистка всего кеша меняет состояние системы, но не объясняет стоимость формирования. После очистки первый запрос закономерно может быть тяжёлым. Выключение кеша убирает один путь и добавляет нагрузку на базу и PHP. Это не диагностика.
\nУвеличение CACHE_TIME может уменьшить число построений, но закрепит неверный HTML, если ключ неполон. И наоборот, добавление в ключ каждого доступного признака создаст лишние варианты и усложнит обновление. Включайте только те значения, которые действительно меняют результат.
Переписывать шаблон только потому, что он виден в каталоге файлов, тоже рискованно. Шаблон отвечает за вывод, но не обязан отвечать за дорогой запрос. Не смешивайте в одной правке кеш, SQL, PHP и браузер. Иначе положительный результат нельзя связать с одним изменением. Если гипотеза не подтверждается, верните именно этот diff и повторите исходный вход.
\nДокументация Bitrix описывает API кеширования, но не знает самописный компонент, версию PHP, структуру базы, настройки окружения и реальные условия пользователя. Учебный код не заменяет профиль. Названия IBLOCK_ID, SECTION_ID, LANGUAGE_ID и группы в примере не являются данными конкретного сайта.
Нельзя объявлять страницу быстрой только по факту действующего кеша. Компонент может быть быстрым, а задержка останется в другом блоке, на уровне сети, браузера или внешнего сервиса. Статья также не утверждает, что любая Bitrix-страница станет быстрее после добавления зависимости или изменения времени кеша.
\nДиагностика готова, когда выбран один воспроизводимый маршрут, назван компонент и шаблон, перечислены входы, которые меняют результат и кеш, а также сохранён артефакт до и после одного обратимого изменения. Действие считается успешным только при повторном замере того же входа и при отсутствии нового нарушения содержимого или доступа. Если хотя бы одного условия нет, готов не результат, а следующий вопрос.
\nМатериал датирован ноябрём 2022 года, поэтому для API приведены архивные снимки официальной документации. Они фиксируют состояние страниц до этой даты. Текущая документация может содержать обновления и не используется здесь как доказательство исторического состояния. Собственный учебный пример показывает контракт рассуждения, но не выдаётся за результат работы конкретного сервера.
\ntrue/false, базовые зависимости кеша и дополнительный идентификатор.$arResult, сохраняемых при встроенном кешировании компонентов.