1С Битрикс · 7 мин чтения

Кеширование в Битрикс: управляемый кеш и теги на практике

Кеширование в Битрикс - первое, что я проверяю, когда клиент жалуется на просевший TTFB на карточках товаров или листингах с фильтром. В девяти случаях из десяти окажется, что кеш либо выключен на самых тяжёлых компонентах, либо настроен без тегов, из-за чего его чистят целиком при любом чихе в админке. Управляемый кеш в связке с тегами - это единственный вариант, который реально снимает нагрузку с MySQL на высоконагруженном интернет-магазине, не превращая при этом сайт в помойку из устаревших цен и остатков.

Зачем нужно кеширование в Битрикс и как оно устроено

Битрикс кеширует данные на нескольких уровнях: кеш компонентов, кеш результатов запросов (D7 ORM), HTML-кеш и композитный кеш всей страницы. Каждый уровень решает свою задачу, и путать их - частая причина, почему кеш «не работает» или работает не так, как ожидалось.

Файлы кеша по умолчанию лежат в /bitrix/cache/, но на проде я почти всегда переключаю их в память через memcache или redis - модуль cache_type в настройках производительности меняется без переписывания кода компонентов. На проекте с каталогом в 40 000 товаров переход с файлового кеша на redis снизил среднее время генерации листинга с 380 мс до 90 мс просто за счёт устранения дисковых операций на чтение множества мелких файлов.

Важно различать TTL и теги. TTL - это просто время жизни кеша в секундах, грубая защита от бесконечного устаревания данных. Теги - инструмент точечного управления: они позволяют сбросить конкретный кусок кеша именно тогда, когда изменились связанные с ним данные, а не ждать, пока истечёт таймер, и не сбрасывать всё разом.

Управляемый кеш компонентов: StartResultCache и параметры

Базовый механизм - $this->StartResultCache() внутри класса компонента. Он принимает время жизни, дополнительный ключ кеширования (обычно массив параметров запроса) и признак использования агрегированного кеша.

if ($this->StartResultCache($this->arParams['CACHE_TIME'], $additionalCacheId)) {
    $res = CIBlockElement::GetList(
        $arParams['SORT'],
        $arParams['FILTER']
    );
    while ($ob = $res->GetNextElement()) {
        $this->arResult['ITEMS'][] = $ob->GetFields();
    }
    $this->SetResultCacheKeys(['ITEMS']);
    $this->IncludeComponentTemplate();
}

Если внутри блока кеширования выполнялось действие, которое не должно попадать в кеш (например, счётчик просмотров или проверка авторизации пользователя), нужно вызвать $this->AbortResultCache() - иначе эти данные заморозятся в кеше на весь TTL и будут показываться всем пользователям одинаково, что на практике вызывает баги вида «у всех в шапке чужое имя».

CACHE_TYPE в параметрах компонента имеет три значения: A (авто, зависит от настроек модуля), Y (кешировать всегда) и N (не кешировать). На боевых проектах я почти всегда ставлю Y на компонентах каталога и меню, и N - там, где логика завязана на конкретного пользователя без явного разделения по ключам кеша.

Теги кеша: точечная очистка вместо сноса всего разом

Теги решают проблему, из-за которой управляемый кеш без них почти бесполезен на реальном магазине: любое изменение одного товара сбрасывает TTL и заставляет ждать, либо администратор нажимает «очистить кеш» руками и сбрасывает вообще всё, включая никак не связанные разделы.

if ($this->StartResultCache($arParams['CACHE_TIME'], false, $this->__component_name)) {
    CIBlockElement::GetList($sort, $filter, false, false, $select);
    
    global $taggedCache;
    $taggedCache->StartTagCache('/');
    $taggedCache->RegisterTag('iblock_id_' . $iblockId);
    foreach ($arResult['ITEMS'] as $item) {
        $taggedCache->RegisterTag('iblock_element_' . $item['ID']);
    }
    $taggedCache->EndTagCache();

    $this->IncludeComponentTemplate();
}

При изменении элемента инфоблока Битрикс сам вызывает ClearByTag('iblock_element_' . $ID) из обработчика событий модуля iblock - это уже встроено в ядро для стандартных компонентов. Но если вы пишете свой компонент или кастомный обработчик через agent.php, регистрацию и очистку тегов нужно прописывать вручную, и именно тут разработчики чаще всего забывают про RegisterTag внутри цикла по товарам, оставляя только тег на весь инфоблок - тогда любое изменение цены одного товара чистит кеш листинга целиком, что при каталоге в десятки тысяч позиций съедает CPU на пересборку.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

HTML-кеш и композитный кеш: в чём разница на практике

HTML-кеш компонента (тот, что настраивается через StartResultCache с сохранением уже отрендеренного шаблона) экономит и запросы к базе, и время на PHP-рендеринг. Композитный кеш работает на уровень выше - кеширует всю страницу целиком, включая HTML вокруг компонентов, и подменяет для авторизованных пользователей только динамические зоны через AJAX-довставку.

Тип кеша Что кеширует Когда применять
Кеш компонента (StartResultCache) Результат запроса + HTML шаблона компонента Каталог, меню, баннеры, любые повторяющиеся блоки
Кеш D7 ORM (CachedQuery) Результат конкретного запроса к таблице Кастомные highload-блоки, свои сущности
HTML-кеш (опция модуля main) Готовый HTML целой страницы или её части Статичные лендинги, посадочные страницы без персонализации
Композитный кеш Вся страница с выделением динамических зон Магазины с авторизацией, где большая часть контента общая для всех

На проекте с интеграцией СДЭК я обычно не кеширую сам расчёт стоимости доставки - он завязан на адрес и вес корзины пользователя, это разовый запрос к API, - но кеширую список городов и пунктов выдачи на 6-12 часов через обычный TagCache с тегом cdek_pvz_list. Это убирает 90% обращений к внешнему сервису на популярных направлениях, а точность данных страдает не критично, потому что список ПВЗ обновляется у СДЭК не каждый час.

Автоочистка кеша при изменении данных инфоблоков

Стандартный сценарий: карточка товара обновляется в 1С и подтягивается в Битрикс через обмен, но кеш листинга категории не сбрасывается, потому что обработчик обмена меняет поля напрямую через CIBlockElement::SetPropertyValuesEx в обход событий OnAfterIBlockElementUpdate. Итог - на витрине висит старая цена ещё сутки, пока не истечёт TTL.

Я решаю это явной очисткой по тегам сразу после обмена:

use BitrixMainDataTaggedCache;

$taggedCache = BitrixMainApplication::getInstance()->getTaggedCache();
$taggedCache->clearByTag('iblock_id_' . $iblockId);
$taggedCache->clearByTag('iblock_element_' . $elementId);

Если обмен идёт через внешний сервис или n8n-сценарий, который дергает вебхук после импорта прайса, финальным шагом сценария добавляю HTTP-запрос на служебный обработчик, который делает clearByTag по списку изменённых ID - это надёжнее, чем полагаться на встроенные события Битрикс, особенно когда импорт идёт через прямые SQL-запросы или BulkInsert в обход API инфоблоков. Если нужно завязать такую очистку кеша на внешние системы или собрать под задачу готовый обработчик, у меня в библиотеке готовых скриптов есть похожие примеры автообработчиков, которые можно адаптировать под конкретный инфоблок.

Частые ошибки при работе с кешем в Битрикс

Самая частая - CACHE_TYPE=N на компонентах с высокой посещаемостью просто потому, что разработчик один раз словил баг с устаревшими данными и отключил кеш вместо того, чтобы разобраться с тегами. Вторая по частоте - кеширование персонализированного контента без разделения по ключу: цена со скидкой конкретного пользователя, статус его заказа или содержимое корзины попадают в общий HTML-кеш и утекают к другим посетителям.

Третья ошибка - забытый AbortResultCache() в блоках с условной логикой. Если внутри StartResultCache есть ветка, зависящая от сессии или GET-параметров, не отражённых в дополнительном ключе кеша, первый посетитель со специфичным запросом «застолбит» результат для всех остальных на весь TTL.

Четвёртая - TTL в сутки или больше на страницах с ценами при интеграции с эквайрингом или маркетплейсами, где цены меняются несколько раз в день. Здесь разумнее короткий TTL в 5-15 минут плюс теги на реальные события изменения, чем один длинный таймер без сброса по событию.

Мониторинг и отладка кеша на живом проекте

Битрикс даёт встроенный монитор в разделе Настройки → Инструменты разработчика → Производительность, где видно количество файлов кеша и объём. На проектах с большим трафиком я смотрю не на это, а на реальные метрики: время ответа php-fpm до и после включения кеша на конкретном компоненте, замеренное через встроенный профайлер (&CHAR(38);show_sql_stat=Y в URL для админа).

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

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

Чтобы сайт работал без сбоев

Техподдержка

от 15 000 ₽/мес

Подробнее →

Частые вопросы

Чем управляемый кеш в Битрикс отличается от обычного кеша компонентов?

Обычный кеш компонентов работает по TTL и хранит результат заданное время независимо от того, изменились данные или нет. Управляемый кеш - это тот же механизм StartResultCache, но с включённой поддержкой тегов через модуль main, что даёт возможность сбрасывать конкретные записи по событию, а не ждать истечения таймера.

Нужно ли включать композитный кеш, если на сайте есть личный кабинет?

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

Как быстро сбросить кеш только одного раздела каталога, не трогая остальной сайт?

Если на компоненты раздела навешаны теги вида iblock_section_ID, достаточно вызвать clearByTag с этим тегом из административного обработчика или агента - весь остальной кеш сайта останется нетронутым, и пересборке подвергнется только затронутый раздел.

Почему кеш в Битрикс иногда не очищается даже после явного вызова clearByTag?

Чаще всего причина в том, что тег регистрировался с другим именем или регистром символов, либо кеш хранится не в файлах, а в memcache/redis с отдельным неймспейсом, который не совпадает с тем, что использует функция очистки. Стоит сверить точное имя тега в коде регистрации и в коде очистки - расхождение в один символ ломает всю логику.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно