Кеширование в компонентах включается галочкой, а вот собственный код кешируется вручную. Тяжёлая выборка, обращение к внешнему API, сложный расчёт — всё это должно выполняться раз в час, а не при каждом открытии страницы.
В D7 для этого есть Bitrix\Main\Data\Cache. Разберём базовый шаблон
использования, работу с тегированным кешем и типичные ошибки, из-за которых
кеш либо не работает, либо отдаёт устаревшие данные.
Базовый шаблон
Конструкция всегда одинаковая — три метода и условие:
<?php
use Bitrix\Main\Data\Cache;
$cache = Cache::createInstance();
$cacheTime = 3600; // секунд
$cacheId = 'catalog_top_' . $sectionId;
$cachePath = '/catalog/top/';
if ($cache->initCache($cacheTime, $cacheId, $cachePath)) {
// кеш есть и не устарел — берём готовое
$items = $cache->getVars()['items'];
} elseif ($cache->startDataCache()) {
// кеша нет — считаем
$items = getExpensiveData($sectionId);
// и сохраняем
$cache->endDataCache(['items' => $items]);
}
Разберём параметры:
| Параметр | Назначение |
|---|---|
| Время жизни | Сколько секунд кеш считается актуальным |
| Идентификатор | Уникальный ключ. Обязан включать все параметры выборки |
| Путь | Подпапка в хранилище кеша — упрощает выборочную очистку |
Идентификатор — главное место ошибок
Ключ кеша должен зависеть от всего, что влияет на результат. Если выборка зависит от раздела, языка и группы пользователя, все три параметра обязаны попасть в ключ.
Плохо:
<?php
$cacheId = 'catalog_items'; // один кеш на все разделы
Хорошо:
<?php
$cacheId = md5(serialize([
'section' => $sectionId,
'page' => $page,
'groups' => $USER->GetUserGroupArray(),
'lang' => LANGUAGE_ID,
]));
Забытый параметр приводит к самому неприятному классу багов: часть пользователей видит чужие данные. Особенно опасно забыть группы пользователя, когда от них зависят цены или доступность товаров.
Обратная крайность тоже вредна: если добавить в ключ что-то уникальное для каждого посетителя — например, идентификатор сессии, — кеш перестанет попадать вообще, а хранилище будет забиваться мусором.
Нужна помощь с Битрикс?
Тегированный кеш
Обычный кеш живёт до истечения времени. Но если товар отредактировали, ждать час бессмысленно — данные уже устарели.
Тегированный кеш решает это: кеш помечается тегом, а при изменении данных все записи с этим тегом сбрасываются.
<?php
use Bitrix\Main\Data\Cache;
use Bitrix\Main\Application;
$cache = Cache::createInstance();
$taggedCache = Application::getInstance()->getTaggedCache();
if ($cache->initCache(3600, $cacheId, $cachePath)) {
$items = $cache->getVars()['items'];
} elseif ($cache->startDataCache()) {
$taggedCache->startTagCache($cachePath);
$taggedCache->registerTag('iblock_id_' . $iblockId);
$items = getExpensiveData();
$taggedCache->endTagCache();
$cache->endDataCache(['items' => $items]);
}
Тег iblock_id_{ID} — стандартный: Битрикс сбрасывает его сам
при изменении любого элемента инфоблока. Свои теги можно сбрасывать вручную:
<?php
$taggedCache->clearByTag('my_custom_tag');
Важное условие: тегированный кеш должен быть включён в настройках главного модуля. Если он выключен, регистрация тегов ничего не делает, и кеш будет жить до истечения времени.
Очистка кеша
<?php
// очистить конкретную запись
$cache->clean($cacheId, $cachePath);
// очистить всю подпапку
$cache->cleanDir($cachePath);
Именно ради выборочной очистки стоит использовать осмысленные пути:
/catalog/top/ удобнее, чем всё в корне хранилища.
Что кешировать, а что нет
| Операция | Кешировать |
|---|---|
| Тяжёлая выборка из базы | Да |
| Запрос к внешнему API | Да, обязательно |
| Построение дерева разделов | Да |
| Сложный расчёт по многим записям | Да |
| Данные корзины пользователя | Нет |
| Персональные данные | Нет |
| Простой запрос по первичному ключу | Обычно не нужно |
| Курс валют из внешнего сервиса | Да, с коротким временем |
Особый случай — внешние API. Кеш здесь не только ускоряет, но и защищает: если сторонний сервис лёг, сайт продолжит работать на закешированных данных до истечения срока.
Где физически лежит кеш
По умолчанию — в файлах в папке /bitrix/cache/. На нагруженных проектах
файловое хранилище становится узким местом, и его переносят в память:
- Memcached — быстрый, простой, подходит большинству проектов;
- Redis — умеет больше, устойчивее к перезапускам.
Что важно: код при этом не меняется. Cache::createInstance()
сам использует настроенное хранилище. Настройка описана в статьях
Настройка Memcached в Битрикс
и Redis для Битрикс.
Отладка
Проверить, попадает ли код в кеш, проще всего логированием ветки пересчёта:
<?php
if ($cache->initCache($cacheTime, $cacheId, $cachePath)) {
$items = $cache->getVars()['items'];
} elseif ($cache->startDataCache()) {
\Bitrix\Main\Diag\Debug::writeToFile(
'кеш пересчитан: ' . $cacheId,
'CACHE',
'/local/logs/cache.log'
);
$items = getExpensiveData();
$cache->endDataCache(['items' => $items]);
}
Если в логе запись появляется при каждом обновлении страницы — кеш не работает. Обычно причина в том, что идентификатор содержит что-то уникальное для каждого запроса.
Частые ошибки
- Неполный идентификатор. Пользователи видят данные из чужого контекста.
- Уникальный идентификатор на каждый запрос. Кеш не попадает никогда, хранилище растёт.
-
Забытый
endDataCache(). Результат не сохраняется, расчёт идёт каждый раз. - Кеширование персональных данных. Самая опасная ошибка: один пользователь получает информацию другого.
- Слишком долгое время жизни без тегов. Контент обновили, а сайт сутки показывает старое.
- Регистрация тегов при выключенном тегированном кеше. Работает как обычный кеш, а разработчик ждёт автоматического сброса.
Итог
Bitrix\Main\Data\Cache кеширует результаты собственного кода:
тяжёлые выборки, обращения к внешним сервисам и сложные расчёты. Схема неизменна —
initCache, startDataCache, endDataCache.
Ключевое требование — правильный идентификатор: в него должны входить все параметры, влияющие на результат, включая группы пользователя. А чтобы кеш сбрасывался при изменении данных, а не по таймеру, используйте тегированный кеш — при условии, что он включён в настройках модуля.
