8 lines
20 KiB
JSON
8 lines
20 KiB
JSON
{
|
||
"index": 186,
|
||
"slug": "editorial-2022-11-practice-bitrix-performance",
|
||
"title": "Производительность Bitrix-страницы: как найти узкое место без оптимизации наугад",
|
||
"excerpt": "Разделяем маршрут, компонент, кеш и шаблон, сверяем зависимость результата и выбираем одно обратимое действие вместо отключения кеша вслепую.",
|
||
"contentHtml": "<p>Страница каталога открывается медленно, а в разговоре звучит одно объяснение: «тормозит Bitrix». Такой диагноз ничего не проверяет. Он не показывает, какой URL воспроизводит симптом, какой компонент формирует блок, где срабатывает кеш и какой шаблон отдаёт HTML. Цена ошибки — лишние запросы к базе, отключённый кеш, переписанный шаблон и тот же медленный экран.</p>\n<p>Начинайте с наблюдаемого факта. Для одного входа запишите маршрут, компонент, шаблон, параметры и условие, при котором HTML меняется. Если часть сведений неизвестна, оставьте её неизвестной. Это лучше, чем заменить пробел догадкой. Производительность страницы нельзя объяснить одним слоем: запрос, компонент, кеш и рендеринг связаны, но проверяются отдельно.</p>\n<h2>Сначала отделите границы, потом меняйте код</h2>\n<p>Одна правка должна отвечать на один вопрос. Например: «входит ли группа пользователя в зависимость кеша этого компонента?» Это проверяемый вопрос. «Почему Bitrix медленный?» — нет. Пока вопрос не ограничен, нельзя выбрать ни инструмент, ни безопасное действие.</p>\n<p>Разделите страницу на четыре границы. Маршрут задаёт вход. Компонент принимает параметры и получает данные. Кеш решает, нужно ли снова выполнять вычисление и сохраняет ли результат. Шаблон превращает результат компонента в HTML. Большой HTML не доказывает медленный SQL. Наличие кеша в настройках не доказывает попадание в кеш на нужном запросе. Видимый шаблон не доказывает, что он стал причиной задержки.</p>\n<div class='table-scroll'><table><caption>Что проверять до правки страницы</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Гипотеза</th><th scope='col'>Проверка</th><th scope='col'>Безопасное действие</th></tr></thead><tbody><tr><td>Один и тот же каталог долго формирует ответ</td><td>Дорогая ветка компонента или промах кеша</td><td>Повторить URL и записать компонент, параметры и режим кеша</td><td>Получить один разрешённый прикладной артефакт, не меняя TTL</td></tr><tr><td>Разные пользователи видят разный HTML</td><td>Группа, право или сегмент не вошли в ключ</td><td>Сверить условия вывода с дополнительной зависимостью и параметрами</td><td>Добавить зависимость или остановить изменение до уточнения контракта</td></tr><tr><td>После очистки кеша первый запрос снова тяжёлый</td><td>Очистка убрала результат, но не стоимость построения</td><td>Сравнить холодный и повторный вход на одном маршруте</td><td>Искать стоимость формирования, а не считать очистку исправлением</td></tr><tr><td>Кеш работает, но HTML избыточен</td><td>В результат попадают лишние данные</td><td>Проверить структуру <code>arResult</code> и список ключей результата</td><td>Сократить результат только после проверки шаблона и владельца данных</td></tr><tr><td>После изменения исчезли данные</td><td>Нарушена зависимость результата или изменён шаблон</td><td>Повторить исходный вход и сравнить HTML и условия доступа</td><td>Вернуть один diff и разобрать недостающий вход</td></tr></tbody></table></div>\n<h2>Что именно гарантирует компонентный кеш</h2>\n<p>Встроенное кеширование Bitrix начинается с <code>StartResultCache</code>. При действующем кеше метод возвращает <code>false</code>, выводит сохранённое содержимое и заполняет <code>$arResult</code>. При недействительном кеше он возвращает <code>true</code>; компонент получает данные, подключает шаблон, а результат сохраняется при вызове <code>IncludeComponentTemplate</code> или <code>ShowComponentTemplate</code>. Это описание ветки компонента, а не замер всей страницы.</p>\n<p>Документация называет базовые части зависимости: сайт, имя компонента, имя шаблона и входные параметры <code>$arParams</code>. Дополнительное условие передают через <code>additionalCacheID</code>. В официальном примере в него входят группы пользователя. Если HTML зависит от права, языка или другого значения, это значение должно участвовать в зависимости результата. Иначе один сохранённый HTML может попасть к другому варианту посетителя.</p>\n<p><code>SetResultCacheKeys</code> решает другой вопрос. Метод перечисляет поля <code>$arResult</code>, которые должны быть доступны при использовании встроенного кеша. Он не измеряет SQL, не уменьшает автоматически время рендера и не превращает тяжёлый компонент в быстрый. Его смысл появляется только после проверки того, какие данные нужны шаблону и коду вокруг компонента.</p>\n<figure><img src='/assets/editorial/2022/bitrix-performance-2022-request-contract.svg' alt='Схема разбора производительности Bitrix-страницы: маршрут передаёт вход компоненту, компонент проверяет кеш и формирует HTML через шаблон; на каждой границе фиксируется отдельный факт.' loading='lazy' /><figcaption>Один запрос проходит несколько границ. Объявленная зависимость кеша помогает проверить контракт, но не заменяет измерение времени ответа.</figcaption></figure>\n<h2>Учебный пример с согласованным результатом</h2>\n<p>Ниже показана сокращённая схема. Функция <code>loadCatalogItems</code> условна: в проекте её нужно заменить реальным чтением данных. Группа и язык включены в дополнительный идентификатор только потому, что предположительно меняют HTML. Если это не так, их добавление лишь дробит кеш и не даёт полезной защиты.</p>\n<pre><code>$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}</code></pre>\n<p>Здесь <code>null</code> означает ошибочный или недопустимый вход, а пустой массив может быть нормальным пустым каталогом. Это различие важно: пустое состояние, которое можно безопасно показать всем посетителям с одинаковым ключом, не нужно искусственно объявлять ошибкой. <code>AbortResultCache</code> применяйте только когда результат действительно нельзя сохранять.</p>\n<p>Массив <code>ITEMS</code> нужен шаблону в ветке построения. Поля <code>SECTION_ID</code> и <code>ITEM_COUNT</code> перечислены отдельно, потому что код после компонента или модификатор результата может обращаться именно к ним. Это не универсальный список. Если шаблон зависит от другой части результата, сначала назовите её и проверьте её место в контракте.</p>\n<p>Вызов с <code>false</code> в первом аргументе означает, что время кеширования берётся из <code>$arParams['CACHE_TIME']</code>. Ненулевой <code>CACHE_TIME</code> ещё не доказывает попадание в кеш. Для такого вывода нужен отдельный артефакт: профиль, лог или разрешённый замер на том же маршруте и с тем же вариантом входа.</p>\n<h2>Порядок диагностики</h2>\n<ol><li><strong>Зафиксируйте симптом.</strong> Укажите один URL, вариант запроса и наблюдаемое поведение. Не добавляйте выдуманные миллисекунды, SQL или долю попаданий в кеш.</li><li><strong>Назовите границу.</strong> Найдите подключаемый компонент, его шаблон и параметры. Отдельно запишите условия, которые могут менять HTML или доступ к данным.</li><li><strong>Сверьте контракт кеша.</strong> Проверьте базовые входы и дополнительные зависимости <code>StartResultCache</code>. Сопоставьте их с фактическими условиями вывода.</li><li><strong>Разделите пустой и ошибочный результат.</strong> Решите, является ли пустой каталог валидным состоянием или признаком недопустимого входа. Только во втором случае рассматривайте <code>AbortResultCache</code>.</li><li><strong>Выберите один артефакт.</strong> Используйте доступный в проекте лог, профиль, трассировку или замер. Он должен различать две гипотезы, например промах кеша и дорогую подготовку данных.</li><li><strong>Сделайте один узкий diff.</strong> Меняйте только один параметр, зависимость или шаблон. До изменения запишите, что вернуть, если результат не подтверждён.</li><li><strong>Повторите тот же вход.</strong> Сравните прежний и новый артефакт на том же URL, варианте пользователя и наборе данных. Если вход изменился, это новый эксперимент.</li></ol>\n<h2>Быстрые исправления, которые скрывают причину</h2>\n<p>Очистка всего кеша меняет состояние системы, но не объясняет стоимость формирования. После очистки первый запрос закономерно может быть тяжёлым. Выключение кеша убирает один путь и добавляет нагрузку на базу и PHP. Это не диагностика.</p>\n<p>Увеличение <code>CACHE_TIME</code> может уменьшить число построений, но закрепит неверный HTML, если ключ неполон. И наоборот, добавление в ключ каждого доступного признака создаст лишние варианты и усложнит обновление. Включайте только те значения, которые действительно меняют результат.</p>\n<p>Переписывать шаблон только потому, что он виден в каталоге файлов, тоже рискованно. Шаблон отвечает за вывод, но не обязан отвечать за дорогой запрос. Не смешивайте в одной правке кеш, SQL, PHP и браузер. Иначе положительный результат нельзя связать с одним изменением. Если гипотеза не подтверждается, верните именно этот diff и повторите исходный вход.</p>\n<h2>Ограничения и критерий готовности</h2>\n<p>Документация Bitrix описывает API кеширования, но не знает самописный компонент, версию PHP, структуру базы, настройки окружения и реальные условия пользователя. Учебный код не заменяет профиль. Названия <code>IBLOCK_ID</code>, <code>SECTION_ID</code>, <code>LANGUAGE_ID</code> и группы в примере не являются данными конкретного сайта.</p>\n<p>Нельзя объявлять страницу быстрой только по факту действующего кеша. Компонент может быть быстрым, а задержка останется в другом блоке, на уровне сети, браузера или внешнего сервиса. Статья также не утверждает, что любая Bitrix-страница станет быстрее после добавления зависимости или изменения времени кеша.</p>\n<p>Диагностика готова, когда выбран один воспроизводимый маршрут, назван компонент и шаблон, перечислены входы, которые меняют результат и кеш, а также сохранён артефакт до и после одного обратимого изменения. Действие считается успешным только при повторном замере того же входа и при отсутствии нового нарушения содержимого или доступа. Если хотя бы одного условия нет, готов не результат, а следующий вопрос.</p>\n<h2>Граница источников для ноября 2022 года</h2>\n<p>Материал датирован ноябрём 2022 года, поэтому для API приведены архивные снимки официальной документации. Они фиксируют состояние страниц до этой даты. Текущая документация может содержать обновления и не используется здесь как доказательство исторического состояния. Собственный учебный пример показывает контракт рассуждения, но не выдаётся за результат работы конкретного сервера.</p>\n<h2>Проверяемые источники</h2><ul><li><a href='https://web.archive.org/web/20220927080127id_/https://dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/startresultcache.php' target='_blank' rel='noopener noreferrer'>1С-Битрикс: CBitrixComponent::StartResultCache, снимок 27 сентября 2022 года</a> — описывает возврат <code>true</code>/<code>false</code>, базовые зависимости кеша и дополнительный идентификатор.</li><li><a href='https://web.archive.org/web/20220815073221id_/https://dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/setresultcachekeys.php' target='_blank' rel='noopener noreferrer'>1С-Битрикс: CBitrixComponent::SetResultCacheKeys, снимок 15 августа 2022 года</a> — описывает список ключей <code>$arResult</code>, сохраняемых при встроенном кешировании компонентов.</li><li><a href='https://web.archive.org/web/20220629225339id_/https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&LESSON_ID=3485' target='_blank' rel='noopener noreferrer'>1С-Битрикс: курс о кешировании, снимок 29 июня 2022 года</a> — различает компонентное, неуправляемое и управляемое кеширование; это не профиль и не замер конкретной страницы.</li></ul>"
|
||
}
|