8 lines
18 KiB
JSON
8 lines
18 KiB
JSON
{
|
||
"index": 185,
|
||
"slug": "editorial-2022-11-mechanism-bitrix-performance",
|
||
"title": "Почему кешированный компонент Bitrix не гарантирует быструю страницу",
|
||
"excerpt": "Медленная Bitrix-страница начинается не с размера JavaScript. Разберите маршрут, компонент, ключ кеша и шаблон, чтобы найти измеряемую причину и не нарушить выдачу для разных состояний пользователя.",
|
||
"contentHtml": "<p>Каталог открывается заметно дольше обычного. В DevTools виден большой HTML, сервер возвращает ответ без явной ошибки, а команда спорит о размере JavaScript. Через час отключают кеш компонента. Время почти не меняется, зато база получает больше запросов. Пользователь по-прежнему ждёт, а команда теряет защиту от повторной работы.</p>\n<p>Цена ошибки здесь двойная. Неверная оптимизация не убирает задержку. Неполный ключ кеша может отдать одному посетителю HTML, рассчитанный для другого состояния. Поэтому фраза «компонент закеширован» ничего не говорит о полной скорости страницы и сама по себе не доказывает корректность результата.</p>\n<p>Рабочий тезис такой: сначала нужно восстановить контракт результата, затем измерить названную границу. Для одного маршрута разделите вход запроса, компонент, решение о встроенном кеше и шаблон. У каждого слоя должен быть свой факт проверки. Если зависимость HTML неизвестна, изменение кеша останавливается.</p>\n<h2>Механизм: что делает встроенный кеш компонента</h2>\n<p><code>CBitrixComponent::StartResultCache</code> отвечает за ветку компонента. При действительном кеше метод возвращает <code>false</code> и использует сохранённый результат. При недействительном кеше он возвращает <code>true</code>, после чего компонент получает данные и подключает шаблон. Документация называет базовые части зависимости: <code>SITE_ID</code>, имя компонента, имя шаблона и входные <code>$arParams</code>. Дополнительное условие передают отдельно через второй параметр.</p>\n<p>Этот контракт описывает решение компонента. Он не сообщает, сколько занял PHP, какой запрос выполнила база, прочитался ли файл кеша и сколько времени занял браузер. Вызов с ожидаемой веткой повторного использования тоже не равен наблюдаемому cache hit на конкретном HTTP-запросе. Для такого вывода нужен отдельный артефакт: профиль, лог или другой разрешённый инструмент среды.</p>\n<p><code>SetResultCacheKeys</code> решает соседнюю задачу. Метод определяет, какие части <code>$arResult</code> сохраняются при встроенном кешировании. Если его не вызвать, ядро сериализует весь результат. Это влияет на объём и состав сохранённых данных. Метод не добавляет отсутствующее условие в ключ и не доказывает, что шаблон перестал влиять на HTML.</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>URL и одинаковый вариант входа</td><td>Какой PHP или SQL исполнился</td><td>Повторить один вход</td></tr><tr><td>Компонент</td><td>Имя и фактические параметры</td><td>Что он единственный источник задержки</td><td>Назвать владельца</td></tr><tr><td>Кеш</td><td>Режим и полный список зависимостей</td><td>Реальный hit-rate и время чтения</td><td>Сверить ключ с output</td></tr><tr><td>Шаблон</td><td>Имя и данные, которые он выводит</td><td>Его длительность в миллисекундах</td><td>Выбрать измерение границы</td></tr></tbody></table></div>\n<h2>Как неполный ключ ломает результат</h2>\n<p>Представим каталог, где блок цены зависит от сегмента посетителя. Компонент получает <code>IBLOCK_ID</code>, сортировку и постраничность. Эти параметры входят в <code>$arParams</code>. Сегмент хранится отдельно и влияет на HTML: один посетитель видит персональную цену, другой — обычную.</p>\n<p>Если сегмент не участвует в зависимости, два разных результата становятся одним кешированным результатом. Первый запрос создаёт HTML. Следующий запрос может получить его, хотя условие вывода изменилось. Очистка кеша исправит уже сохранённое значение только временно. При следующем построении ошибка повторится. Увеличение TTL делает риск дольше, а отключение кеша маскирует его ценой дополнительных запросов.</p>\n<p>Проверяйте каждую переменную по выходу компонента. Если она меняет HTML, она должна быть частью контракта результата: через <code>$arParams</code> или через дополнительный идентификатор зависимости. Если переменная влияет только на срок обновления данных, это другой список. Не смешивайте ключ результата и invalidation.</p>\n<figure><img src=\"/assets/editorial/2022/bitrix-performance-2022-cache-contract.svg\" alt=\"Схема контракта кеша Bitrix: сайт, компонент, шаблон, параметры и дополнительное условие проходят к HTML\" loading=\"lazy\" /><figcaption>Схема показывает границы контракта. Она не является профилем сервера и не показывает реальный cache hit.</figcaption></figure>\n<h2>Учебный пример на PHP</h2>\n<p>Пример показывает безопасную форму проверки. Он не измеряет production, не обращается к Bitrix и не утверждает, что конкретный URL использует этот путь. В реальном компоненте состав <code>$extraCacheId</code> нужно получить из фактических условий, которые меняют HTML.</p>\n<pre><code>$extraCacheId = implode(':', [\n (string) $visitorSegment,\n (string) $priceMode,\n]);\n\nif ($this->StartResultCache(false, $extraCacheId)) {\n $arResult = loadCatalogItems($arParams);\n\n if ($arResult['ITEMS'] === []) {\n $this->AbortResultCache();\n } else {\n $this->SetResultCacheKeys(['ITEMS', 'SECTION_ID']);\n $this->IncludeComponentTemplate();\n }\n}</code></pre>\n<p>Здесь дополнительный идентификатор участвует только потому, что сегмент и режим цены объявлены входами, меняющими выдачу. Код не должен собирать ключ из случайных данных, которые не относятся к результату. Если значение недоступно на этой границе, безопасный путь — не угадывать его, а остановить изменение и найти источник условия.</p>\n<p><code>AbortResultCache</code> нужен для отрицательного результата, который не следует сохранять как обычную выдачу. Например, запись может отсутствовать, а запрос с произвольным идентификатором не должен заполнять кеш бесконечными пустыми вариантами. Конкретное решение зависит от компонента. Главное — не считать любой выход из ветки успешным построением кеша.</p>\n<h2>Симптом → причина → проверка → действие</h2>\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>Разделить TTFB, PHP, SQL, HTML и браузер разрешённым профилем</td><td>Вернуть кеш и измерить названную границу</td></tr><tr><td>Разные посетители видят один вариант блока</td><td>Условие HTML не вошло в ключ</td><td>Сравнить output contract и дополнительные параметры</td><td>Добавить зависимость или временно запретить кеширование</td></tr><tr><td>Кеш большой и медленно обновляется</td><td>В <code>$arResult</code> сохраняются лишние ветки</td><td>Проверить вызов <code>SetResultCacheKeys</code> и данные шаблона</td><td>Оставить только нужные ключи после проверки шаблона</td></tr><tr><td>После очистки проблема возвращается</td><td>Исправлен симптом, а не контракт</td><td>Повторить сценарий после нового построения</td><td>Найти источник меняющегося HTML</td></tr><tr><td>Страница стала другой после замены шаблона</td><td>Сравнение сделано с другим output</td><td>Сопоставить входы, шаблон и сегмент посетителя</td><td>Откатить один diff и начать сравнение заново</td></tr></tbody></table></div>\n<h2>Порядок проверки</h2>\n<ol><li>Зафиксируйте один маршрут, параметры запроса и состояние пользователя. Не меняйте URL, сегмент и пагинацию между сравнениями.</li><li>Назовите компонент и шаблон. Найдите место вызова <code>StartResultCache</code>, второй параметр и вызовы <code>SetResultCacheKeys</code>.</li><li>Составьте список всех значений, которые могут менять HTML. Отдельно отметьте источник каждого значения.</li><li>Сверьте список с <code>$arParams</code> и дополнительным идентификатором кеша. При пропуске не меняйте TTL и не очищайте весь кеш как «проверку».</li><li>Выберите один наблюдаемый артефакт для одной границы: разрешённый профиль, лог, SQL trace или замер ответа. Запишите условия замера.</li><li>Измените один параметр или один шаблон. До изменения назовите rollback: прежний шаблон, прежний ключ или прежняя настройка.</li><li>Повторите тот же сценарий с теми же входами. Сравните корректность HTML и выбранный показатель, а не только субъективное ощущение.</li><li>Если зависимость не доказана, верните изменение и оформите недостающий факт. Остановка — результат проверки, а не неудача.</li></ol>\n<h2>Ограничения</h2>\n<p>Встроенный кеш компонента ускоряет только ту работу, которую он действительно может повторно использовать. Он не устраняет медленный SQL, тяжёлый PHP, большой ответ, блокирующий ресурс браузера или внешний API. Страница может иметь несколько компонентов с разными ключами и сроками жизни. Один успешный компонентный кеш не описывает весь запрос.</p>\n<p>Нельзя выводить production-результат из учебного кода. Без замера не называйте миллисекунды, процент ускорения, hit-rate и экономию ресурсов. Без проверки выдачи не объявляйте ключ полным. Без владельца значения не добавляйте его в зависимость наугад: случайный ключ ухудшит повторное использование и усложнит invalidation.</p>\n<p>Отрицательный путь обязателен. Если внешний признак меняет HTML, но его источник и область действия неясны, изменение блокируется. Если доступного измерения нет, можно проверить контракт и корректность, но нельзя заявлять улучшение скорости. В таком случае следующий шаг — получить один названный артефакт на согласованном стенде.</p>\n<h2>Критерий готовности</h2>\n<p>Изменение готово, когда для одного маршрута выполнены четыре условия. Команда перечисляет все входы, меняющие HTML. Каждый вход попадает в согласованный контракт или явно исключён как не влияющий на результат. Выбранный профиль или замер повторяется с теми же условиями и показывает сравнимый показатель. После изменения проверены разные состояния пользователя и назван rollback.</p>\n<p>Если хотя бы одно условие не выполнено, результатом должна быть остановка или дополнительное наблюдение, а не утверждение «страница ускорена». Такая граница сохраняет корректность выдачи и не позволяет одному неясному симптому превратиться в глобальную настройку кеша.</p>\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/startresultcache.php\" target=\"_blank\" rel=\"noopener noreferrer\">1С-Битрикс: CBitrixComponent::StartResultCache</a> — официальный API-описатель веток кеширования и базовых зависимостей.</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/setresultcachekeys.php\" target=\"_blank\" rel=\"noopener noreferrer\">1С-Битрикс: CBitrixComponent::SetResultCacheKeys</a> — официальный API-описатель состава сохраняемого <code>$arResult</code>.</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/main/reference/cbitrixcomponent/abortresultcache.php\" target=\"_blank\" rel=\"noopener noreferrer\">1С-Битрикс: CBitrixComponent::AbortResultCache</a> — официальный API-описатель отказа от сохранения результата.</li></ul>"
|
||
}
|