diff --git a/editorial/agent-rewrites/186.json b/editorial/agent-rewrites/186.json index 35d52dc..ac46c62 100644 --- a/editorial/agent-rewrites/186.json +++ b/editorial/agent-rewrites/186.json @@ -2,6 +2,6 @@ "index": 186, "slug": "editorial-2022-11-practice-bitrix-performance", "title": "Производительность Bitrix-страницы: как найти узкое место без оптимизации наугад", - "excerpt": "Разделяем маршрут, компонент, кеш и шаблон, проверяем зависимость результата и выбираем одно обратимое действие вместо отключения кеша вслепую.", - "contentHtml": "

Страница каталога открывается медленно, а в разговоре звучит только одно объяснение: «тормозит Bitrix». Такой диагноз ничего не проверяет. Он не показывает, какой URL воспроизводит симптом, какой компонент формирует блок, где срабатывает кеш и какой шаблон отдаёт HTML. Цена ошибки — лишние запросы к базе, отключённый кеш, переписанный шаблон и тот же медленный экран. После нескольких изменений команда уже не знает, что именно помогло или что безопасно вернуть.

\n

Начинайте с наблюдаемого факта. Запишите маршрут, вариант входа, компонент, шаблон и условие, при котором HTML меняется. Если часть сведений неизвестна, оставьте её неизвестной. Это лучше, чем заменить пробел догадкой. Производительность страницы нельзя объяснить одним слоем: запрос, компонент, кеш и рендеринг связаны, но проверяются отдельно.

\n

Тезис: сначала отделите границы, потом меняйте код

\n

Одна правка должна отвечать на один вопрос. Например: «входит ли группа пользователя в зависимость кеша этого компонента?» Это проверяемый вопрос. «Почему Bitrix медленный?» — нет. Пока вопрос не ограничен, нельзя выбрать ни инструмент, ни безопасное действие.

\n

Разделите страницу на четыре границы. Маршрут задаёт вход. Компонент принимает параметры и получает данные. Кеш решает, нужно ли снова выполнять вычисление и сохраняет ли результат. Шаблон превращает результат компонента в HTML. Большой HTML не доказывает медленный SQL. Наличие кеша в настройках не доказывает cache hit на нужном запросе. Видимый шаблон не доказывает, что он стал причиной задержки.

\n
Что проверять до правки страницы
СимптомВозможная причинаПроверкаДействие
Один и тот же каталог долго формирует ответДорогая ветка компонента или промах кешаПовторить тот же URL и записать компонент, параметры и режим кешаПолучить один разрешённый серверный или прикладной артефакт, не меняя TTL
Разные пользователи видят разный HTMLГруппа, право или сегмент не вошли в ключСверить все условия output с additionalCacheID и параметрамиДобавить зависимость или остановить изменение до уточнения контракта
После очистки кеша первый запрос снова тяжёлыйОчистка убрала результат, но не устранила стоимость построенияСравнить холодный и повторный вход на одном маршрутеИскать стоимость формирования, а не считать очистку исправлением
Кеш работает, но HTML всё ещё избыточенВ результат попадают лишние данныеПроверить, нужен ли компоненту полный arResult и вызван ли SetResultCacheKeysСократить сохраняемые данные только после проверки шаблона
После изменения исчезают данные для части посетителейНарушена зависимость результата или изменён шаблонПовторить исходный вход и сравнить HTML и условия доступаВернуть один diff и разобрать недостающий input
\n

Как устроено кеширование компонента

\n

Встроенное кеширование Bitrix начинается с StartResultCache. При действующем кеше метод возвращает false и отдаёт сохранённый результат. При недействующем кеше он возвращает true, после чего компонент получает данные и подключает шаблон. По документации базовая зависимость включает сайт, имя компонента, имя шаблона и входные параметры $arParams. Дополнительное условие передают отдельно.

\n

Это контракт, а не измерение скорости. Он говорит, от чего должен зависеть результат, но не сообщает, сколько заняло выполнение запроса и был ли конкретный запрос cache hit. Если HTML зависит от группы пользователя, права доступа, языка или другого значения, такого условия нельзя оставлять только в PHP-ветке. Оно должно участвовать в зависимости результата. Иначе один сохранённый HTML может попасть к другому варианту посетителя.

\n

SetResultCacheKeys решает другую задачу. Метод задаёт поля $arResult, которые нужно сохранить для использования после чтения кеша. Если его не вызвать внутри участка с StartResultCache, ядро может сериализовать весь результат. Лишние данные увеличивают размер кеша, но сам факт большого кеша ещё не доказывает, что он является главным узким местом страницы.

\n
\"Схема
Один запрос проходит несколько границ. Проверяйте их по отдельности: объявленная зависимость кеша не заменяет измерение времени ответа.
\n

Учебный пример компонента

\n

Ниже показана сокращённая схема, а не готовый код для конкретного проекта. Число инфоблока, параметры и поле группы вы должны заменить фактическими значениями. Пример нужен, чтобы увидеть место проверки зависимости и отрицательный путь. Он не сообщает production-результаты и не измеряет время.

\n
if ($this->StartResultCache(false, [$USER->GetGroups(), $arParams['LANGUAGE_ID']])) {\n    $this->arResult = loadCatalogItems($arParams['IBLOCK_ID']);\n\n    if (!$this->arResult) {\n        $this->AbortResultCache();\n        return;\n    }\n\n    $this->SetResultCacheKeys(['SECTION_ID', 'ITEM_COUNT']);\n    $this->IncludeComponentTemplate();\n}
\n

В этом учебном варианте группа пользователя и язык входят в дополнительную зависимость, потому что они могут менять результат. Пустой результат останавливает кеширование через AbortResultCache: иначе отрицательный или неполный ответ может сохраниться как обычный. Нельзя переносить этот список в проект без проверки. Если HTML зависит от другой переменной, её нужно назвать и проверить отдельно.

\n

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

\n

Порядок диагностики

\n
  1. Зафиксируйте симптом. Укажите один URL, вариант запроса и наблюдаемое поведение. Не добавляйте выдуманные миллисекунды, SQL или cache hit-rate.
  2. Назовите границу. Найдите подключаемый компонент, его шаблон и параметры. Отдельно запишите условия, которые могут менять HTML или доступ к данным.
  3. Сверьте контракт кеша. Проверьте базовые входы и дополнительные зависимости StartResultCache. Не называйте кеш рабочим только потому, что в параметрах стоит ненулевой CACHE_TIME.
  4. Выберите один артефакт. Используйте доступный в проекте лог, профиль, трассировку или замер. Он должен различать две гипотезы: например, промах кеша и дорогую подготовку данных.
  5. Сделайте один узкий diff. Меняйте только один параметр, зависимость или шаблон. До изменения запишите, что вернуть, если результат не подтверждён.
  6. Повторите тот же вход. Сравните прежний и новый артефакт на том же URL, варианте пользователя и наборе данных. Если вход изменился, это новый эксперимент.
\n

Быстрые исправления, которые скрывают причину

\n

Очистка всего кеша меняет состояние системы, но не объясняет стоимость формирования. После очистки первый запрос закономерно может быть тяжёлым. Выключение кеша убирает один путь и добавляет нагрузку на базу и PHP. Это не диагностика. Увеличение CACHE_TIME может уменьшить число построений, но закрепит неверный HTML, если ключ неполон.

\n

Переписывать шаблон только потому, что он виден в каталоге файлов, тоже рискованно. Шаблон отвечает за вывод, но не обязан отвечать за дорогой запрос. Сначала проверьте, что компонент уже получил данные и сколько данных он сохраняет. И наоборот: уменьшение arResult не исправит медленный SQL, если запрос выполняется до формирования результата.

\n

Не смешивайте в одной правке кеш, SQL, PHP и браузер. Иначе положительный результат нельзя связать с одним изменением. Если гипотеза не подтверждается, верните именно этот diff и повторите исходный вход. Откат всей страницы или массовая очистка кеша уничтожают полезный контекст.

\n

Ограничения и критерий готовности

\n

Документация Bitrix описывает API кеширования, но не знает самописный компонент, версию PHP, структуру базы, настройки окружения и реальные условия пользователя. Учебный код не заменяет профиль. Названия IBLOCK_ID, LANGUAGE_ID и группы в примере не являются данными конкретного сайта. Статья также не утверждает, что любая Bitrix-страница станет быстрее после добавления кеша.

\n

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

\n

Проверяемые источники

" + "excerpt": "Разделяем маршрут, компонент, кеш и шаблон, сверяем зависимость результата и выбираем одно обратимое действие вместо отключения кеша вслепую.", + "contentHtml": "

Страница каталога открывается медленно, а в разговоре звучит одно объяснение: «тормозит Bitrix». Такой диагноз ничего не проверяет. Он не показывает, какой URL воспроизводит симптом, какой компонент формирует блок, где срабатывает кеш и какой шаблон отдаёт HTML. Цена ошибки — лишние запросы к базе, отключённый кеш, переписанный шаблон и тот же медленный экран.

\n

Начинайте с наблюдаемого факта. Для одного входа запишите маршрут, компонент, шаблон, параметры и условие, при котором HTML меняется. Если часть сведений неизвестна, оставьте её неизвестной. Это лучше, чем заменить пробел догадкой. Производительность страницы нельзя объяснить одним слоем: запрос, компонент, кеш и рендеринг связаны, но проверяются отдельно.

\n

Сначала отделите границы, потом меняйте код

\n

Одна правка должна отвечать на один вопрос. Например: «входит ли группа пользователя в зависимость кеша этого компонента?» Это проверяемый вопрос. «Почему Bitrix медленный?» — нет. Пока вопрос не ограничен, нельзя выбрать ни инструмент, ни безопасное действие.

\n

Разделите страницу на четыре границы. Маршрут задаёт вход. Компонент принимает параметры и получает данные. Кеш решает, нужно ли снова выполнять вычисление и сохраняет ли результат. Шаблон превращает результат компонента в HTML. Большой HTML не доказывает медленный SQL. Наличие кеша в настройках не доказывает попадание в кеш на нужном запросе. Видимый шаблон не доказывает, что он стал причиной задержки.

\n
Что проверять до правки страницы
СимптомГипотезаПроверкаБезопасное действие
Один и тот же каталог долго формирует ответДорогая ветка компонента или промах кешаПовторить URL и записать компонент, параметры и режим кешаПолучить один разрешённый прикладной артефакт, не меняя TTL
Разные пользователи видят разный HTMLГруппа, право или сегмент не вошли в ключСверить условия вывода с дополнительной зависимостью и параметрамиДобавить зависимость или остановить изменение до уточнения контракта
После очистки кеша первый запрос снова тяжёлыйОчистка убрала результат, но не стоимость построенияСравнить холодный и повторный вход на одном маршрутеИскать стоимость формирования, а не считать очистку исправлением
Кеш работает, но HTML избыточенВ результат попадают лишние данныеПроверить структуру arResult и список ключей результатаСократить результат только после проверки шаблона и владельца данных
После изменения исчезли данныеНарушена зависимость результата или изменён шаблонПовторить исходный вход и сравнить HTML и условия доступаВернуть один diff и разобрать недостающий вход
\n

Что именно гарантирует компонентный кеш

\n

Встроенное кеширование Bitrix начинается с StartResultCache. При действующем кеше метод возвращает false, выводит сохранённое содержимое и заполняет $arResult. При недействительном кеше он возвращает true; компонент получает данные, подключает шаблон, а результат сохраняется при вызове IncludeComponentTemplate или ShowComponentTemplate. Это описание ветки компонента, а не замер всей страницы.

\n

Документация называет базовые части зависимости: сайт, имя компонента, имя шаблона и входные параметры $arParams. Дополнительное условие передают через additionalCacheID. В официальном примере в него входят группы пользователя. Если HTML зависит от права, языка или другого значения, это значение должно участвовать в зависимости результата. Иначе один сохранённый HTML может попасть к другому варианту посетителя.

\n

SetResultCacheKeys решает другой вопрос. Метод перечисляет поля $arResult, которые должны быть доступны при использовании встроенного кеша. Он не измеряет SQL, не уменьшает автоматически время рендера и не превращает тяжёлый компонент в быстрый. Его смысл появляется только после проверки того, какие данные нужны шаблону и коду вокруг компонента.

\n
Схема разбора производительности Bitrix-страницы: маршрут передаёт вход компоненту, компонент проверяет кеш и формирует HTML через шаблон; на каждой границе фиксируется отдельный факт.
Один запрос проходит несколько границ. Объявленная зависимость кеша помогает проверить контракт, но не заменяет измерение времени ответа.
\n

Учебный пример с согласованным результатом

\n

Ниже показана сокращённая схема. Функция loadCatalogItems условна: в проекте её нужно заменить реальным чтением данных. Группа и язык включены в дополнительный идентификатор только потому, что предположительно меняют HTML. Если это не так, их добавление лишь дробит кеш и не даёт полезной защиты.

\n
$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 применяйте только когда результат действительно нельзя сохранять.

\n

Массив ITEMS нужен шаблону в ветке построения. Поля SECTION_ID и ITEM_COUNT перечислены отдельно, потому что код после компонента или модификатор результата может обращаться именно к ним. Это не универсальный список. Если шаблон зависит от другой части результата, сначала назовите её и проверьте её место в контракте.

\n

Вызов с false в первом аргументе означает, что время кеширования берётся из $arParams['CACHE_TIME']. Ненулевой CACHE_TIME ещё не доказывает попадание в кеш. Для такого вывода нужен отдельный артефакт: профиль, лог или разрешённый замер на том же маршруте и с тем же вариантом входа.

\n

Порядок диагностики

\n
  1. Зафиксируйте симптом. Укажите один URL, вариант запроса и наблюдаемое поведение. Не добавляйте выдуманные миллисекунды, SQL или долю попаданий в кеш.
  2. Назовите границу. Найдите подключаемый компонент, его шаблон и параметры. Отдельно запишите условия, которые могут менять HTML или доступ к данным.
  3. Сверьте контракт кеша. Проверьте базовые входы и дополнительные зависимости StartResultCache. Сопоставьте их с фактическими условиями вывода.
  4. Разделите пустой и ошибочный результат. Решите, является ли пустой каталог валидным состоянием или признаком недопустимого входа. Только во втором случае рассматривайте AbortResultCache.
  5. Выберите один артефакт. Используйте доступный в проекте лог, профиль, трассировку или замер. Он должен различать две гипотезы, например промах кеша и дорогую подготовку данных.
  6. Сделайте один узкий diff. Меняйте только один параметр, зависимость или шаблон. До изменения запишите, что вернуть, если результат не подтверждён.
  7. Повторите тот же вход. Сравните прежний и новый артефакт на том же URL, варианте пользователя и наборе данных. Если вход изменился, это новый эксперимент.
\n

Быстрые исправления, которые скрывают причину

\n

Очистка всего кеша меняет состояние системы, но не объясняет стоимость формирования. После очистки первый запрос закономерно может быть тяжёлым. Выключение кеша убирает один путь и добавляет нагрузку на базу и PHP. Это не диагностика.

\n

Увеличение CACHE_TIME может уменьшить число построений, но закрепит неверный HTML, если ключ неполон. И наоборот, добавление в ключ каждого доступного признака создаст лишние варианты и усложнит обновление. Включайте только те значения, которые действительно меняют результат.

\n

Переписывать шаблон только потому, что он виден в каталоге файлов, тоже рискованно. Шаблон отвечает за вывод, но не обязан отвечать за дорогой запрос. Не смешивайте в одной правке кеш, SQL, PHP и браузер. Иначе положительный результат нельзя связать с одним изменением. Если гипотеза не подтверждается, верните именно этот diff и повторите исходный вход.

\n

Ограничения и критерий готовности

\n

Документация Bitrix описывает API кеширования, но не знает самописный компонент, версию PHP, структуру базы, настройки окружения и реальные условия пользователя. Учебный код не заменяет профиль. Названия IBLOCK_ID, SECTION_ID, LANGUAGE_ID и группы в примере не являются данными конкретного сайта.

\n

Нельзя объявлять страницу быстрой только по факту действующего кеша. Компонент может быть быстрым, а задержка останется в другом блоке, на уровне сети, браузера или внешнего сервиса. Статья также не утверждает, что любая Bitrix-страница станет быстрее после добавления зависимости или изменения времени кеша.

\n

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

\n

Граница источников для ноября 2022 года

\n

Материал датирован ноябрём 2022 года, поэтому для API приведены архивные снимки официальной документации. Они фиксируют состояние страниц до этой даты. Текущая документация может содержать обновления и не используется здесь как доказательство исторического состояния. Собственный учебный пример показывает контракт рассуждения, но не выдаётся за результат работы конкретного сервера.

\n

Проверяемые источники

" }