ГлавнаяБлогКэширование в 1С-Битрикс: 5 механизмов и когда какой

Кэширование в 1С-Битрикс: 5 механизмов и когда какой

Рамиль Юналиев
Рамиль Юналиев
E-Commerce Lead
27 июля 2026 г.
11 мин чтения

В Битрикс не один вид кэша, а пять разных механизмов, которые решают разные задачи и почти не пересекаются друг с другом. Путаница обычно начинается именно здесь: разработчик включает автокэш компонента, а тормозит совсем другой запрос, который автокэш не касается. Разбираю все пять по порядку — что кэширует, как включается, как сбрасывается.

Какие виды кэша есть в Битрикс

МеханизмЧто кэшируетУровень
Кэш данных (CPHPCache / D7 Cache)Результат произвольного PHP-кода — массив, объект, HTML-фрагментКод разработчика
Автокэш компонентаРезультат и шаблон компонента целикомКомпонент
Тегированный кэшТо же самое, что кэш данных, но с точечным сбросом по тегу вместо TTLКод разработчика / компонент
Композитный сайтГотовая HTML-страница целикомСтраница
Хранилище (файлы / memcached / redis)Не вид кэша, а то, где физически лежат данные всех кэшей вышеИнфраструктура

Путь одного запроса — если на каком-то уровне кэш есть, ответ отдаётся сразу и ниже запрос не идёт:

Запрос
  │
  ▼
Страница закэширована композитным сайтом?  ──да──▶  отдаём готовую страницу
  │ нет
  ▼
Компонент закэширован автокэшем (CACHE_TYPE)?  ──да──▶  отдаём HTML компонента
  │ нет
  ▼
Данные есть в кэше данных / тегированном кэше?  ──да──▶  отдаём данные, рендерим шаблон
  │ нет
  ▼
База данных

Дальше — по каждому пункту.

Кэш данных — CPHPCache и его D7-аналог

Базовый механизм: положить в файл результат тяжёлой операции и не пересчитывать его заново, пока не истечёт время жизни. Класс CPHPCache — рабочий, никуда не убран, но это API старого ядра. В D7 у него есть прямой аналог с тем же порядком вызовов — \Bitrix\Main\Data\Cache.

Пример на CPHPCache и разбор параметров ($sCacheID, $sCacheTime, BXClearCache()) — в старом посте про кэш. Здесь — как то же самое выглядит в D7:

// получаем объект кэша D7 — прямой аналог CPHPCache
$cache = \Bitrix\Main\Data\Cache::createInstance();
 
// идентификатор должен включать все параметры, влияющие на результат выборки
$cacheId = "news_list_" . $iblockId . "_" . $pageNum;
 
// папка кэша относительно /bitrix/cache/
$cachePath = "/news_list/";
 
// пробуем открыть кэш на 3600 секунд по этому ID и пути
if ($cache->initCache(3600, $cacheId, $cachePath)) {
    // кэш валиден — забираем сохранённые ранее данные, в базу не ходим
    $items = $cache->getVars();
} elseif ($cache->startDataCache()) {
    // кэша нет или устарел — включаем запись нового кэша
    $items = getNewsFromDb($iblockId, $pageNum);
 
    // сохраняем результат в файл кэша под тем же ID
    $cache->endDataCache($items);
}

Методы initCache() / startDataCache() / endDataCache() / getVars() — тёзки методов CPHPCache, логика идентична. Разница только в пространстве имён (неймспейсе) и стиле вызова: методы вызываются у объекта ($cache->initCache(...)), а не статически через new CPHPCache.

Важное про $cacheId не меняется от версии к версии: в идентификатор должны попадать все параметры, от которых зависит результат выборки — но только подготовленные, типизированные значения, а не сырые данные из $_GET. Если в идентификатор подставить необработанный параметр запроса, кто угодно сможет «раздуть» кэш, гоняя мусорные значения — на диске накопится множество бесполезных файлов.

Автокэширование компонентов

У каждого компонента есть встроенный кэш, который включается не в коде, а в .parameters.php:

$arComponentParameters = [
    'PARAMETERS' => [
        // A — кэшировать, если глобально включено «Автокэширование компонентов»
        'CACHE_TYPE' => ['DEFAULT' => 'A'],
        // время жизни кэша в секундах
        'CACHE_TIME' => ['DEFAULT' => 3600],
    ],
];

CACHE_TIME — секунды. CACHE_TYPE — три значения, и здесь чаще всего путают A с Y:

  • N — компонент не кэшируется никогда.
  • A («авто») — компонент кэшируется, если в Настройки → Настройки продукта → Автокеширование (вкладка «Настройка кеширования компонентов») включена галочка глобально. Выключена галочка — компонент с CACHE_TYPE=A работает так, как будто у него N, независимо от CACHE_TIME.
  • Y — компонент кэшируется всегда, глобальную галочку игнорирует.

Внутри компонента управлять кэшем можно двумя методами:

class MyComponent extends CBitrixComponent
{
    public function executeComponent()
    {
        // true — кэша нет/устарел, нужно собрать $arResult заново
        // false — кэш уже отдан, весь блок ниже пропускается
        if ($this->startResultCache()) {
            $this->arResult['ITEMS'] = $this->getItems();
 
            if (empty($this->arResult['ITEMS'])) {
                // данных нет — не сохраняем «пустой» результат в кэш
                $this->AbortResultCache();
            } else {
                // в кэш идёт только ключ ITEMS, а не весь $arResult
                $this->SetResultCacheKeys(['ITEMS']);
            }
 
            // рендер шаблона — при попадании в кэш эта строка не выполнится
            $this->includeComponentTemplate();
        }
    }
}

SetResultCacheKeys() — задаёт, какие именно ключи $arResult сериализовать в кэш; без вызова сериализуется весь массив. AbortResultCache() — отменяет кэширование по факту выборки, например когда искомый элемент не найден: без этого метода компонент закэшировал бы «пусто» и отдавал бы пустой результат все CACHE_TIME секунд, даже если элемент появится.

Кэшируется здесь не «данные» и «шаблон» раздельно — это один слой: при попадании в кэш шаблон компонента и result_modifier.php вообще не выполняются, из файла кэша достаётся готовый HTML.

Тегированный кэш D7

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

$taggedCache = \Bitrix\Main\Application::getInstance()->getTaggedCache();
 
$taggedCache->startTagCache('/my/path/');
$taggedCache->registerTag('iblock_id_' . $iblockId);
$taggedCache->endTagCache();
 
// сброс — где угодно в другом месте кода, при изменении данных
\Bitrix\Main\Application::getInstance()->getTaggedCache()->clearByTag('iblock_id_' . $iblockId);

Методы класса \Bitrix\Main\Data\TaggedCache:

  • startTagCache($path) — открывает блок регистрации тегов для указанного пути кэша.
  • registerTag($tag) — помечает: текущий кэш зависит от этого тега.
  • endTagCache() — закрывает блок, теги записываются в базу.
  • clearByTag($tag) — сбрасывает все кэши, помеченные этим тегом.

Управляемый кэш — именно так называется эта обвязка в самой админке, и для компонентов она включается сама. Статус — в Настройки → Настройки продукта → Автокеширование, вкладка «Настройка управляемого кеширования»; в коде за это отвечает константа BX_COMP_MANAGED_CACHE. Когда управляемый кэш активен, startResultCache() из прошлого раздела автоматически оборачивает компонент в startTagCache()/endTagCache(). Не активен — компонент кэшируется по TTL, но без тегов, точечный сброс не сработает.

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

Highload-блоки (HL-блоки) и кэш — почему не сбрасывается «само»

У инфоблоков регистрация тегов встроена в саму выборку: CIBlockResult::Fetch() вызывает CIBlock::registerWithTagCache($iblockId), а тот — $CACHE_MANAGER->RegisterTag("iblock_id_" . $iblockId). Поэтому кэш компонента, читающего элементы инфоблока, сбрасывается сам при изменении элемента — тег регистрируется прозрачно, без единой строчки со стороны разработчика.

У highload-блоков этого нет: HighloadBlockTable наследуется от базового ORM-класса DataManager, а он тегов не регистрирует — это функциональность именно модуля iblock. Значит выборка из HL-блока в тегированном кэше сама не инвалидируется при изменении записи — тег приходится сбрасывать вручную.

Проще всего — сразу после записи:

use Bitrix\Main\Application;
 
// добавляем запись в HL-блок через стандартный ORM D7
$result = MyHlBlockTable::add($fields);
 
if ($result->isSuccess()) {
    // ORM highload-блоков теги сама не сбрасывает — делаем это вручную,
    // тег должен совпадать со строкой, которую регистрировали при чтении
    Application::getInstance()->getTaggedCache()->clearByTag('hlblock_' . MyHlBlockTable::getHighloadBlock()['ID']);
}

А при чтении регистрировать тот же тег через registerTag() внутри startTagCache() / endTagCache(), как в разделе выше. Строку с тегом нужно продублировать и на чтении, и на записи — иначе сброс просто не найдёт, что чистить.

Композитный сайт — кэш страницы целиком

Композитный кэш — не про данные, а про готовую страницу целиком. Включается в Настройки → Настройки продукта → Композитный сайт, режимы — «Автокомпозит» или «Композит». Собранный HTML сохраняется в /bitrix/html_pages/, отдельно по доменам; хранить можно на диске или в оперативной памяти через memcached (в памяти кэш пропадает при перезапуске сервера).

Страница целиком статична не значит, что на ней не может быть личных данных — для этого есть динамические зоны, помечаемые не тегом в разметке, а вызовом API прямо в шаблоне компонента (template.php):

// в шаблоне компонента — создаём динамическую зону с уникальным ID
$frame = $this->createFrame('user-greeting')->begin();
 
echo 'Здравствуйте, ' . $USER->GetFullName();
 
// закрываем зону — дальше снова обычный статический контент
$frame->end();

Весь вывод между begin() и end() не попадает в статический HTML и подгружается отдельным AJAX-запросом после отдачи страницы из кэша.

Сброс — кнопкой «Сбросить кэш» в настройках или программно: \Bitrix\Main\Composite\Page::getInstance()->deleteAll(). Отдельно есть cron_html_pages.php — но это не сброс по требованию, а фоновая уборка: он удаляет с диска файлы старше заданного времени, не разбирая, актуальны они ещё или нет. Диагностика — раздел «Композитный сайт → Страницы» показывает по каждому URL число просмотров и перезаписей: если перезаписей много относительно просмотров, это сигнал, что кэш сбрасывается слишком часто и толком не работает.

Как это выглядит на реальной нагрузке — в разборе 15k+ заказов в месяц без падения: там же про инвалидацию тегов на уровне конкретного элемента, а не всего инфоблока разом.

Где хранится кэш: файлы, memcached, redis

Всё, что описано выше — кэш данных, автокэш компонентов, тегированный кэш — физически пишется через один и тот же слой хранения, который настраивается в bitrix/.settings.php, секция cache. По умолчанию это файлы (CacheEngineFiles). Переключение на memcached:

'cache' => [
    'value' => [
        // какой класс движка кэша использовать вместо файлового
        'type' => [
            'class_name' => '\Bitrix\Main\Data\CacheEngineMemcache',
            // PHP-расширение, которое должно быть установлено на сервере
            'extension' => 'memcache',
        ],
        // адрес и порт демона memcached
        'memcache' => ['host' => '127.0.0.1', 'port' => '11211'],
        // соль — чтобы кэши разных сайтов на одном memcached не пересекались
        'sid' => $_SERVER['DOCUMENT_ROOT'] . '#01',
    ],
],

Для redis — та же структура, другой класс:

'cache' => [
    'value' => [
        // то же самое, но движок — redis
        'type' => [
            'class_name' => '\Bitrix\Main\Data\CacheEngineRedis',
            'extension' => 'redis',
        ],
        // адрес и порт redis-сервера
        'redis' => ['host' => '127.0.0.1', 'port' => '6379'],
        'sid' => $_SERVER['DOCUMENT_ROOT'] . '#01',
    ],
],

Для redis нужно расширение PHP redis (phpredis). Оба класса лежат в ядре, bitrix/modules/main/lib/data/.

Файловый кэш работает нормально на одном сервере. Проблема начинается при нескольких веб-серверах за балансировщиком: файл кэша, записанный на сервере A, физически не существует на сервере B — каждый сервер будет заново пересчитывать один и тот же кэш, и выигрыш от кэша почти пропадает. memcached или redis в этой схеме — общее хранилище, доступное всем серверам одинаково, поэтому кэш, посчитанный один раз, виден сразу везде.

Кэш не сработал — куда смотреть

Порядок проверки, когда компонент явно должен кэшироваться, а каждый раз идёт в базу заново:

  1. Глобальный автокэш выключен. Настройки → Настройки продукта → Автокеширование, вкладка «Настройка кеширования компонентов» — если выключено, все компоненты с CACHE_TYPE=A не кэшируются вообще (см. раздел про автокэш).
  2. $sCacheID меняется на каждый запрос. Обычно из-за необработанного параметра — microtime(), необработанный GET, session ID там, где его быть не должно.
  3. Хранилище недоступно на запись. Права на /bitrix/cache/, либо в .settings.php по ошибке стоит CacheEngineNone (полное отключение кэша, используется для отладки, но иногда забывают вернуть обратно).
  4. CACHE_TIME=0 — компонент технически «кэшируемый», но с нулевым временем жизни, что равносильно отсутствию кэша.
  5. Композитный кэш сброшен глобальным событием. Ищите в разделе «Страницы» частые перезаписи — если весь сайт перезаписывается на каждое изменение одного элемента, вероятно теги стоят не на элемент, а на весь инфоблок или раздел целиком.
  6. HL-блок без ручной инвалидации — если данные меняются, а тегированный кэш вокруг HL-блока не чистится ни при записи, ни при чтении (см. раздел про highload-блоки).

Как измерить эффект кэша — панель производительности

Прежде чем тюнить кэш, стоит увидеть разницу в цифрах, а не на глаз. Встроенная Панель производительности (модуль perfmon) показывает время генерации страницы и число SQL-запросов — поэтому разницу между «включённым CACHE_TYPE=A» и «принудительным N на одном компоненте» сразу видно по числу запросов к базе. Для более глубокого профилирования — Xdebug/Blackfire, если нужно понять, что именно съедает время внутри незакэшированного участка.

Практический способ проверки: временно выставить CACHE_TYPE=N на подозрительном компоненте и сравнить время ответа и число запросов до/после — так сразу видно, действительно ли кэш на этом участке что-то экономит, или тормозит что-то другое.

Частые ошибки кэширования

  • Кэшируют персональные данные без ID пользователя в $cacheId. Один пользователь видит в кэше данные другого — самая неприятная ошибка кэширования из всех.
  • В $cacheId попадает время. Обычно это time(), microtime() или другое постоянно меняющееся значение. Кэш формально «работает», но каждый запрос создаёт новый файл — по сути кэша нет.
  • rand()/mt_rand() внутри закэшированного блока. На кэш-хите отдаётся всегда один и тот же — «случайный» — результат первого запроса, пока кэш не истечёт.
  • Забыли AbortResultCache(). Пустой результат (например «элемент не найден») кэшируется как валидный — элемент появится в базе, а компонент ещё CACHE_TIME секунд будет показывать пустоту.
  • Изменили данные HL-блока и не сбросили тег вручную — см. раздел про HL-блоки выше, это не баг, а ожидаемое поведение, которое легко упустить.

Итог — что когда использовать

ЗадачаИнструмент
Закэшировать выборку в своём кодеКэш данных (CPHPCache / \Bitrix\Main\Data\Cache)
Закэшировать компонент целикомАвтокэш компонента (CACHE_TYPE в .parameters.php)
Сбрасывать кэш точечно, не по TTLТегированный кэш (TaggedCache)
Кэшировать выборку из HL-блокаТегированный кэш + ручной сброс на add/update/delete
Закэшировать страницу целикомКомпозитный сайт
Кэш общий для нескольких серверовmemcached/redis вместо файлового хранилища

Ни один из этих механизмов не заменяет остальные — они закрывают разные уровни одной и той же задачи, и обычно на боевом проекте работают все сразу.