Кеширование в Битрикс - первое, что я проверяю, когда клиент жалуется на просевший 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 с отдельным неймспейсом, который не совпадает с тем, что использует функция очистки. Стоит сверить точное имя тега в коде регистрации и в коде очистки - расхождение в один символ ломает всю логику.