Всё о кешировании в Битрикс
Кеш - специальная область на диске или в операционной памяти компьютера, предназначенная для временного хранения информации и для часто используемых данных и команд. Чаще всего используют файловый кеш, так как он самый простой. Обработанные данные сохраняются в файл на диске, и в будущем данные не обрабатываются, а сразу читаются готовые из этого файла.
При разработке приложений чаще всего узким местом является база данных. Когда данных в таблице много, чтение одних и тех же данных происходит часто - имеет смысл запоминать готовые выборки.
Представим пример, есть страница списка новостей в какой-то категории, первая страница. На странице требуется проверить права доступа пользователя, получить только активные элементы из базы данных, учесть раздел. Новости добавляются 1-2 раза в день, при этом каждый час на странице бывают сотни посетителей.
Чтобы снизить нагрузку на сервер и ускорить страницу, оптимальным вариантом является сохранить результат всех выборок из базы данных и выводить пользователям уже сгенерированный заранее контент. А кеш обновлять только при добавлении новостей.
Принцип работы кеша в Битрикс
В битрикс есть несколько реализаций кеша:
Bitrix\Main\Data\Cache
единый класс для кеширования в новом ядре APID7
(используется)CPageCache
для кеширования только HTML в старом ядре (не используется)CPHPCache
для кеширования HTML и переменных в старом ядре (не используется)
В начале использования кеша мы должны
- Указать время кеширования, по истечению этого срока, старый кеш удалится и создастся новый
- Указать имя кеша и папку хранения. Имя, уникально для каждого элемента, папка может быть общей для однотипных элементов, удалив нужную папку, мы удалим весь кеш какой-то сущности
Далее проверяем имеющийся кеш по нашим параметрам. Если кеша нет, запускаем кеширование и выполняем логику приложения. В случае, когда произошла ошибка и кеш сохранять не надо - можно отменить начатое кеширование. В конце сохраняем результат.
При следующем обращении к функционалу, логика запущена не будет, а результат будет получен от предыдущего выполнения. В этом случае сохраняются ресурсы сервера, а клиент получает ответ быстрее.
Группируются данные по папкам. Если у нас кеша много, но очищать надо всё разом - мы можем сохранять всё в одной папке. Еще в Битрикс можно помечать кеш тегами.
От одной сущности в базе данных может зависеть много файлов кеша и при её редактировании, надо обновить все данные. Чтобы не помнить все зависимости сущности, кеш при создании можно помечать тегами.
Новое универстальное АПИ кеширования в Битрикс на D7
В новом API D7 в Битрикс появился замечательный класс Bitrix\Main\Data\Cache
. Он позволяет кешировать HTML вывод и переменные, пример работы с классом:
<?php
// пространство имен для работы с кешем
use \Bitrix\Main\Data\Cache;
// cлужба кеширования
$cache = Cache::createInstance();
// папка, в которую положем кеш
$cachePath = 'mycachepath';
// срок годности кеша (в секундах)
$cacheTtl = 3600;
// имя кеша (тег)
$cacheKey = 'mycachekey';
// если кеш есть
if ($cache->initCache($cacheTtl, $cacheKey, $cachePath)) {
// получаем кеш в переменную
$vars = $cache->getVars();
// выводим HTML пользователю в браузер
$cache->output();
}
// если кеша нет
elseif ($cache->startDataCache()) {
// тестовые данные
$vars = [
'date' => date('r'),
'rand' => rand(0, 9999), // Если данные закешированы - число не будет меняться
];
// вывод пользователю
echo '<b>Какие-то данные выводятся пользователю (Если кеш не работает, то это число будет меняться: ' . rand(0, 9999) . ')</b>';
// если понимаем что, что-то пошло не так, кеш можно не записывать
$cacheInvalid = false;
if ($cacheInvalid) {
$cache->abortDataCache();
}
// всё хорошо, записываем кеш
$cache->endDataCache($vars);
}
// данные будут обновляться раз в час
print_r($vars);
Чтобы проверить что всё действительно работает:
- Создайте новую страницу на сайте
- В контент вставьте код, который выше
- Откройте страницу в своем браузере (проверьте чтобы в адресной строке не было параметра clear_cache)
- Вы увидите что числа, которые в коде заданы случайными, замирают и не обновляются с обновлением страницы. Это потому что код выполнился 1 раз и результат сохранился
- Если на панели эрмитажа нажать Сбросить кеш - то цифры обновятся
Получение экземпляра класса Bitrix\Main\Data\Cache
$cache = Bitrix\Main\Data\Cache::createInstance()
Методы класса Bitrix\Main\Data\Cache
Статические методы:
\Bitrix\Main\Data\Cache::createInstance()
создает объект для последующей работы не статических методов\Bitrix\Main\Data\Cache::clearCache($initDir)
очищает кеш в папке/bitrix/cache/$initDir
Не статические методы:
-
$cache->initCache($TTL, $uniqueString, $initDir = false, $baseDir = "cache")
- инициализация кеша, проверяет существование кеша, вычитывает данные:$TTL
время жизни в секундах$uniqueString
уникальное имя$initDir
папка, где будет кеш (внутри$baseDir
). Если не указать, то будет браться из текущей запрошенной страницы, для каждой отдельной страницы будет создаваться новый кеш, хотя данные там одни и те же, объем кеша будет огромный, а пользы будет мало, рекомендуется всегда указывать папку$baseDir
папка, внутри/bitrix
, менять не рекомендуется, потому что кеш должен храниться в/bitrix/cache
-
$cache->startDataCache($TTL = false, $uniqueString = false, $initDir = false, $vars = array(), $baseDir = "cache")
начинает процесс кеширования. Дальше данные, которые выводятся в браузер, будут записываться (используется буфер вывода PHP). Все параметры как уinitCache
, только есть еще$vars
:$TTL
время жизни в секундах$uniqueString
уникальное имя$initDir
папка, где будет кеш (внутри$baseDir
). Если не указать, то будет браться из текущей запрошенной страницы, для каждой отдельной страницы будет создаваться новый кеш, хотя данные там одни и те же, объем кеша будет огромный, а пользы будет мало, рекомендуется всегда указывать папку$vars
переменные, которые сохранятся в кеш. Далее в коде переменные можно еще передать вendDataCache()
. Параметр избыточный, передавать сюда данные не рекомендуется. Если вы вызывалиinitCache()
, то параметры вstartDataCache()
передавать уже не стоит, вызывайте без параметров$initDir
папка, где будет кеш (внутри$baseDir
). Если не указать, то будет браться из текущей запрошенной страницы, для каждой отдельной страницы будет создаваться новый кеш, хотя данные там одни и те же, объем кеша будет огромный, а пользы будет мало, рекомендуется всегда указывать папку
$cache->abortDataCache()
останавливает кеширование$cache->endDataCache($vars = false)
сохраняет кеш.$vars
переменные, которые стоит сохранить. Если переменные передаются сюда, то переданные вstartDataCache()
игнорируются$cache->output()
выводит в браузер HTML из кеша$cache->getVars()
получает переменные из кеша$cache->clean($uniqueString, $initDir = false, $baseDir = "cache")
удаляет кеш с именем$uniqueString
в папке$initDir
$cache->cleanDir($initDir = false, $baseDir = "cache")
удаляет весь кеш в папке$initDir
Мы можем не использовать вызов $cache->output()
, если работаем только с переменными. Если работаем только с HTML - так же можем не вызывать $cache->getVars()
.
HTML не требуется передавать в какую-то функцию, чтобы он попал в кеш, просто отдавайте в браузер. Переменные можно сохранять не только скалярные, а все, которые можно сериализовать (массивы, объекты...).
Хранение кеша по умолчанию в /bitrix/cache
.
Чем больше файлов кеша в одной папке верхнего уровня, тем сильнее тормозит очистка кеша и бывает чтение. Поэтому старайтесь группировать кеш по папкам и не создавать их папки каждый раз уникальные.
Тегированный кеш в Битрикс D7 (он же Сache Dependencies)
В дополнение к основному классу кеширования, есть служба для установки тегов на кеш.
Рассмотрим пример с инфоблоком. Есть инфоблок Новости ID 4
, данные из него выводятся на главной странице, на странице списка новостей, так же для каждой новости есть детальная страница. Это всё отдельные файлы кеша. Когда мы добавляем какую-то новость, мы не знаем кеш каких страниц надо сбросить. API инфоблока говорит сбросить кеш по тегу iblock_id_4
, а к тегу уже привязаны десятки файлов, которые удаляются.
Своего кеша у taggedCache
нет, теги хранятся в базе данные в таблице b_cache_tag
.
Пример работы кеширования с тегами:
<?php
// пространство имен для работы с кешем
use \Bitrix\Main\Data\Cache;
use \Bitrix\Main\Application;
// cлужба кеширования
$cache = Cache::createInstance();
// служба пометки кеша тегами
$taggedCache = Application::getInstance()->getTaggedCache();
// чтобы тегированный кеш нашел что ему сбрасывать, необходим одинаковый путь в $cache->initCache() и $taggedCache->startTagCache(), у нас путь указан в $cachePath
$cachePath = 'mycachepath';
$cacheTtl = 3600;
$cacheKey = 'mycachekey';
// если кеш есть
if ($cache->initCache($cacheTtl, $cacheKey, $cachePath)) {
// получаем кеш в переменную
$vars = $cache->getVars();
// тут можно вывести данные в браузер, через $cache->output(); и тогда получится замена классу CPageCache или вернуть return $vars
}
// если кеша нет
elseif ($cache->startDataCache()) {
// начинаем записывать тегированый кеш
$taggedCache->startTagCache($cachePath);
$vars = [
'date' => date('r'),
'rand' => rand(0, 9999), // Если данные закешированы - число не будет меняться
];
// кеш сбрасываем при изменении данных в инфоблоке с ID который передаем
$taggedCache->registerTag('iblock_id_1');
// если понимаем что, что-то пошло не так, кеш можно не записывать
$cacheInvalid = false;
if ($cacheInvalid) {
$taggedCache->abortTagCache();
$cache->abortDataCache();
}
// заканчиваем записывать тегированый кеш
$taggedCache->endTagCache();
// записываем результат в тегированный кеш
$cache->endDataCache($vars);
}
// данные будут обновляться раз в час или при обновлении данных в инфоблоке с ID 1
print_r($vars);
Очистка кеша по тегу:
<?php
// пространство имен для работы с кешем
use \Bitrix\Main\Application;
// служба пометки кеша тегами
$taggedCache = Application::getInstance()->getTaggedCache();
// там где нужно, например на отдельной странице чистим кеш по тегу. В данному случае очистится кеш, который зависит от инфоблока с ID 1
$taggedCache->clearByTag('iblock_id_2');
Получение экземпляра класса taggedCache
$taggedCache = \Bitrix\Main\Application::getInstance()->getTaggedCache();
Методы класса тегированного кеша
$taggedCache->startTagCache($relativePath)
путь, который будет очищаться при удалении кеша по тегу. Путь передавать тот же, что и в$cache->initCache()
$taggedCache->endTagCache()
окончание, сохраняет привязку путей к тегам в базу данных$taggedCache->abortTagCache()
останавливает кеширование. Вызывать когда запущено кеширование, но оно уже не требуется$taggedCache->registerTag($tag)
привязывает тег к пути кеша, указанному ранее при вызовеstartTagCache()
$taggedCache->clearByTag($tag)
очистка кеша по тегу
Стандартные теги в Битрикс
Модуль Универсальные списки lists
:
LISTS_ELEMENT_LIVE_FEED
помечается, но не чиститсяlists_list_{$iblockId}
очищается при редактировании списка, который работает через инфоблок$iblockId
lists_list_any
чистится при редактировании любого списка
Модуль Веб-мессенджер im
:
IM_CONTACT_LIST
при изменении цвета статуса пользователя
Модуль Блог blog
:
blog_post_{$postId}
при изменении постаblog_post_getsocnetperms_{$postId}
при изменении прав поста (при присваивании определенным постам привязок к рабочим группам)
Модуль Обучение learning
:
{$entityTypeId}{$entityId}
при добавлении оценки сущностям, чьи$entityTypeId
равныLEARN_CHAPTER, LEARN_LESSON, LEARN_COURSE. LEARN_COURSE{$courseId}
при обновлении курсаLEARNING_GROUP_{(int) $groupId / 100}
обновление кеша сотни группы. При добавлении/изменении/удалении группыLEARNING_GROUP
при добавлении/изменении/удалении групп
Модуль rest
:
bitrix24_left_menu
при смене прав приложения черезBitrix\Rest\AppTable::setAccess()
Модуль wiki
:
wiki_{$wikiId}
при изменении вики страницы
Модуль forum
:
forum_{$forumId}
forum_topic_{$topicId}
forum_msg_count{$forumId}
при изменений сообщений форума
Модуль инфоблоков iblock
:
iblock_property_enum_{$propertyId}
при изменении свойства типа перечислениеiblock_id_new
при добавлении инфоблоковiblock_id_{$iblockId}
при изменении инфоблока
Модуль социальной сети socialnetwork
:
SONET_LOG_{$logId}
при изменении записи в живой лентеsonet_features_{$entityType}_{$entityId}
при добавлении функционала группеsonet_feature_{$featureId}
при добавлении права на функционалsonet_features2perms_{$permId}
при удалении права на функционалsonet_search_{$entityType}_{$entityId}
sonet_user2group_G{$groupId}
при изменении членства пользователей в группахsonet_user2group_U{$userId}
при изменении членства пользователей в группахsonet_user2group
sonet_group_{$groupId}
при удалении группыsonet_group
при удалении группыsonet_group_activity
при установке активности группыsonet_subscription_{$subscriptionCode}
при добавлении, обновлении подписки. При удалении подписки с указанием кода. При удалении через указание ID не вызываетсяsonet_subscription
при удалении всех подписок некоего пользователяSONET_LOG_FOLLOW_{$userId}
при изменении подписок пользователя в живой лентеblogpost_important_all
при редактировании важных записей{$optionName}{$postId}
при редактировании опций блога пользователя{$optionName}{$postId}_{$userId}}
при редактировании опций блога пользователя{$optionName}_USER_{$userId}}
при редактировании опций блога пользователя
Модуль calendar
:
calendar_user_{$userId}
при изменении событий в календаре пользователяCALENDAR_EVENT_LIST
при изменении раздела в календаре
Модуль Интернет магазин sale
:
sale-location-data
при изменении местоположений
Главный модуль main
:
RV_CACHE
при удалении пользователем оценки{$componentName}
при очистке кеша компонентаUSER_CARD_{$chunkNum}
при изменении пользователя, где$chunkNum = (int) $userId / TAGGED_user_card_size;
кеш не на отдельных пользователей, а на пачкуUSER_CARD
при редактировании пользователя с битриксовой авторизациейEXTERNAL_USER_CARD
при редактировании пользователя с внешней авторизациейUSER_NAME_{$userId}
при изменении пользователя. При обновлении может вызываться не всегда, а только если передано хотя бы одно из следующих полей: NAME, LAST_NAME, SECOND_NAME, ACTIVE, LOGIN, EMAIL, PERSONAL_GENDER, PERSONAL_PHOTO, WORK_POSITION, PERSONAL_PROFESSION, PERSONAL_WWW, PERSONAL_BIRTHDAY, TITLE, EXTERNAL_AUTH_ID, UF_DEPARTMENT, AUTO_TIME_ZONE, TIME_ZONE, TIME_ZONE_OFFSET
Модуль голосований vote
:
vote_form_channel_{$voteId}
при изменении каналов голосованияvote_form_vote_{$voteId}
при изменении голоса, вопросовvote_form_question_{$voteId}
при изменении вопросов, ответовvote_form_answer_{$voteId}
устаревшее
Модуль landing
:
landing_page_{$landingId}
при изменении лендинга
Модуль веб форм form
:
form_{$formId}
при изменении формы, где {$formId} - ID формы
Что делать, если тегов не хватает
Если вам нужны свои теги, добавляйте события, в которых сбрасывайте кеш по своему тегу. Например, нам нужен сброс кеша при редактировании сайтов. Добавим в init.php
такой код:
<?php
AddEventHandler("main", "OnSiteDelete", "clearSiteCacheByTag", 10000);
AddEventHandler("main", "OnBeforeSiteAdd", "clearSiteCacheByTag", 10000);
AddEventHandler("main", "OnBeforeSiteUpdate", "clearSiteCacheByTag", 10000);
function clearSiteCacheByTag() {
$taggedCache = \Bitrix\Main\Application::getInstance()->getTaggedCache();
$taggedCache->clearByTag('site_edit');
}
Теперь при создании, обновлении и удалении сайтов будет удаляться весь кеш по тегу site_edit
.
Управляемый кеш (managed cache)
Почему этот кеш называется управляемый - не ясно, управлять им особо не выходит. Managed cache
в Битрикс это такая прослойка над обычным классом кеширования для более быстрой работы и компактного кода. Используется чаще всего для кеширования выборок отдельных таблиц.
Сразу начну с минусов:
- К этому кешу не привязать теги, без горы костылей
- В старых версиях (где-то до 19 версии) при вызове
cleanAll()
управляемого кеша, удаляется так же весь кеш, на который установлены теги
Простой пример работы с классом:
<?php
// пространство имен для работы с кешем
$managedCache = Bitrix\Main\Application::getInstance()->getManagedCache();
// ключ, по которому сохраняем и получаем данные
$uniqId = 'my_cache_key';
// время жизни кеша
$ttl = 30;
// считываем кеш с диска
$managedCache->read($ttl, $uniqId);
// получаем данные из считанного файла
$res = $managedCache->get($uniqId);
// если данных нет
if ($res === false) {
// генерируем данные, например у нас тут тяжелая логика
$res = date('r');
// сохраняем данные (пока будут храниться в памяти)
$managedCache->set($uniqId, $res);
}
// работаем с данными
var_dump($res);
В примере выше всё просто. Считываем данные, если их нет - с помощью какой-то вашей тяжелой логики генерируем, сохраняем в кеш. В следующий раз, когда данные понадобятся, если срок годности кеша еще не истечет, то получим их из кеша, сэкономив ресурсы.
Сохранение данных на диск происходит в эпилоге битрикс (заключительная часть после отработки логики страницы). Если данные большие, имеет смысл вместо функции set()
использовать setImmediate()
, которая сразу сохраняет данные и очищает память.
Крупные недочеты:
- Пока на диске есть кеш по вашему ключу, новые данные сохранить не получится. Надо сперва удалить старые, а потом вызвать
read()
, хотя мы точно знаем что данных нет.Read()
надо вызывать, потому что там устанавливаетсяttl
, а он обязателен для сохранения - В функции
getImmediate()
требуется указыватьttl
, хотя он не используется.TTL
нужен только при сохранении кеша. При томTTL
не хватает при вызовеsetImmediate()
, там то он нужен. Но так какsetImmediate()
не принимает в параметрахTTL
, перед его вызовом требуется вызовread()
для указанияTTL
Получение экземпляра класса managedCache
$managedCache = Bitrix\Main\Application::getInstance()->getManagedCache();
Методы класса Bitrix\Main\Data\ManagedCache
$managedCache->read($ttl, $uniqueId, $tableId = false)
читает файл кеша, на случай если мы какие-то данные добавим.$ttl
время жизни,$uniqueId
- уникальное имя,$tableId
- папка в которой сохранится файл (относительно/bitrix/managed_cache
). Папку указывать не обязательно$managedCache->getImmediate($ttl, $uniqueId, $tableId = false)
получение данных из кеша без предварительного считывания.$ttl
- ни на что не влияет, не понятно для чего добавили его, остальное как в пункте 1$managedCache->get($uniqueId)
получить данные, которые должны быть предварительно считаны с помощью read функции из пункта 1$managedCache->set($uniqueId, $val)
добавление данных по имени $uniqueId. Чтобы данные добавились и сохранились, предварительно надо вызвать read из пункта 1. Значение может быть как скалярным значением, так и другим, которое можно сериализовать. Данные сохраняются на диск при подключении эпилога$managedCache->setImmediate($uniqueId, $val)
установка значения с последующим моментальным сохранением данных на диск, предварительно должна быть вызваноread()
. Буфер при этом очищается и если вы снова захотите работать с$uniqueId
, то надо будет вызвать зановоread()
$managedCache->clean($uniqueId, $tableId = false)
удаление кеша по имени и папке$managedCache->cleanDir($tableId)
удаление всех кешей в папке$managedCache->cleanAll()
удаление всего управляемого кеша. Где-то до 19 версии битрикс удаляется почему-то еще и весь тегированный кешBitrix\Main\Data\ManagedCache::finalize()
сохранение данных на диск. Автоматически вызывается в эпилоге битрикса
Кеширование ORM запросов и Highload блоков
При разработке модулей часто требуется хранение данных в базе данных, операции при этом одни и те же: Create
, Read
, Update
, Delete
(создание, чтение, обновление и удаление данных). Для быстрой работы с новыми таблицами и стандартизированного подхода создан ORM
, который нужен чтобы не писать каждый раз заново логику валидации данных, генерацию запросов, API получения данных, создания событий.
Для реализации этих целей введены понятия:
- Cущности
Bitrix\Main\Entity\Base
- Поля сущностей
Bitrix\Main\Entity\Field
и его наследники - Датаменеджер
Bitrix\Main\Entity\DataManager
Сущность описывает таблицу в БД. Датаменеджер производит операции выборки и изменения сущности. Более подробно можно почитать в курсе битрикса про ORM
, а мы рассмотрим кеш.
D7
классы можно определить по наличию пространства имен, которое отделяется обратными слешами от имени класса. Например, в старом API работа с группами пользователей идет через класс CGroup
, в новом же через Bitrix\Main\GroupTable
. Наличие символов \
говорит нам о том, что это новый API
. В D7
большинство сущностей построено на основе ORM
, а это нам говорит что наконец у всех классов единая сигнатура
методов. Больше не будет такого что у одних первый параметр это сортировка, у других сортировка это второй параметр, а первый это фильтр.
При использовании новых запросов можно передавать в общем наборе параметров так же ключ cache
с указанием ttl
. Если так сделать, то в первый раз запрос пойдет до базы данных, результат сохранится в кеш, при следующем вызове этого же запроса, данные вернутся из кеша.
$res = \Bitrix\Main\GroupTable::getList([
'cache' => ['ttl' => 3600],
]);
При добавлении/изменении/удалении данных кеш автоматически сбрасывается.
Что не так с HL блоками
Хайлоад блоки тоже работают на основе ORM
, та же сигнатура методов, но вот методы изменения данных переопределены и сброс кеширования туда не добавлен. То есть используете вы код:
<?php
// указываем ID нашего highloadblock блока к которому будет делать запросы
$hlbl = 2;
$hlblock = \Bitrix\Highloadblock\HighloadBlockTable::getById($hlbl)->fetch();
$entity = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock);
$entity_data_class = $entity->getDataClass();
$rsData = $entity_data_class::getList([
'cache' => ['ttl' => 360000],
]);
while($arData = $rsData->Fetch()){
var_dump($arData);
}
Изменяете как-то данные, а кеш по прежнему дает вам устаревшие значения до тех пор, пока не истечет срок его годности. Решается проблема добавлением такого кода в init.php
:
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler('', 'BrandReferenceOnAfterAdd', 'clearBrandReferenceCache');
$eventManager->addEventHandler('', 'BrandReferenceOnAfterUpdate', 'clearBrandReferenceCache');
$eventManager->addEventHandler('', 'BrandReferenceOnAfterDelete', 'clearBrandReferenceCache');
function clearBrandReferenceCache($event)
{
$event->getEntity()->cleanCache();
}
Где BrandReference
- имя вашего HL блока. Данный код удаляет кеш HL блока после изменения данных.
Кеш в компонентах
На основе описанных ниже принципов работают все стандартные компоненты и некоторые от самостоятельных разработчиков. Раздел описывает логику таких компонентов, как bitrix:news.list
, bitrix:news.detail
, bitrix:catalog.section
, bitrix:catalog.element
и других. Для работы кеша в параметры вызова компонента добавляются параметры:
// для разных групп пользователей - один кеш
"CACHE_GROUPS" => "N",
// время кеширования в секундах
"CACHE_TIME" => "36000000",
// тип кеширования. N - выключено, A - включено, кеш автоматически сбрасывается при изменении данных, Y - включено, но кеш сам не сбрасывается
"CACHE_TYPE" => "A",
Важно понимать, что комплексные компоненты не используют кеширование. Они принимают запросы и распределяют между простыми компонентами, которые в свою очередь уже кешируют данные.
В компоненте, если предусмотрено кеширование, всегда вызывается метод startResultCache()
класса CBitrixComponent
. Внутри этого метода, если включено в параметрах вызова компонента, запускается кеширование (обычное и сразу тегированное). Теги нигде тут не устанавливаются.
Если в кешируемой области используется получение данных из инфоблоков, то теги на инфоблок устанавливаются внутри метода Fetch() API
инфоблока. Метод GetNext()
тоже работает поверх Fetch()
, так что тоже ставит тег. Поэтому при работе с инфоблоками, дополнительно ставить теги не требуется.
Если требуется установить дополнительные теги в компоненте, то делать это стоит после вызова startResultCache()
и до includeComponentTemplate()
. В общем виде код компонента выглядит так:
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// валидация параметров
if (!isset($arParams["CACHE_TIME"])) {
$arParams["CACHE_TIME"] = 36000000;
}
$arParams["IBLOCK_TYPE"] = trim($arParams["IBLOCK_TYPE"]);
$arParams["IBLOCK_ID"] = trim($arParams["IBLOCK_ID"]);
$arParams["SORT_BY1"] = trim($arParams["SORT_BY1"]);
// требуется для того, чтобы кеширование сохранило данные из $arResult
$arResult = &$this->arResult;
// начало кеширования, если кеш уже существует - startResultCache() возвращает false, заполняет $arResult, выводит верстку
if ($this->startResultCache(...)) {
// тут код выполняется только когда нет кеша
$iterator = CIBlockElement::GetList(...);
// при вызове GetNext() кеш помечается тегом инфоблока
while ($arItem = $iterator->GetNext()) {
$arResult['ITEMS'][] = $arItem;
}
$this->setResultCacheKeys(array(
"ID",
"IBLOCK_TYPE_ID",
"NAME",
"SECTION",
"ELEMENTS",
));
$this->includeComponentTemplate();
}
// устанавливаются параметры, которые надо выполнять всегда, в независимости от наличия кеша
$APPLICATION->SetTitle($arResult["NAME"]);
Кеширование завершается и сохраняется на диск внутри вызова CBitrixComponent::includeComponentTemplate()
. Записывается HTML вывод шаблона, данные из $arResult
(по фильтру указанному при вызове CBitrixComponent::setResultCacheKeys()
), а так же адреса стилей, файлов js, component_epilog.php, в том числе от вложенных компонентов.
Так как шаблон подключается внутри секции кеширования, в нем тоже можно вызывать CBitrixComponent::setResultCacheKeys()
, указывать теги и даже отменять кеширование.
В шаблоне компонента можно создать файлы script.js
и style.css
. Они будут подключаться автоматически при подключении компонента. Их подключение будет происходить даже когда компонент закеширован.
Методы класса компонентов для кеширования
Методы класса используются одни и те же что при работе через файл component.php
, что через class.php
. В случае работы через class.php
используются $this->arResult
и $this->arParams
.
-
$this->startResultCache($cacheTime = false, $additionalCacheID = false, $cachePath = false)
проверяет наличие кеша компонента, если есть, то заполняет переменную$arResult
, выводитHTML
в браузер и возвращаетfalse
. Если кеша нет, то начинает кеширование, запускает тегированный кеш, возвращаетtrue
. Так же возвращаетtrue
, но не начинает кеширование при выключенном кешировании или если кеширование выбрано авто, но в настройках отключено кеширование компонентов$cacheTime
время кеширования, если переданоfalse
, то берется из$arParams['CACHE_TIME']
$additionalCacheID
кеш зависит от текущего сайтаSITE_ID
, имени компонента, имени шаблона, входных параметров$arParams
. Если кеш должен зависеть от каких-либо дополнительных параметров, то их необходимо передать сюда в виде строки$cachePath
путь к файлу кеша относительно папки кешей. По умолчанию равен/.SITE_ID.<путь к компоненту относительно bitrix/components>/<еще какая-то строка зависящая от переменной состояния>
$this->endResultCache()
завершает кеширование, сохраняет кеш на диск, запоминая в том числеHTML
вывод шаблона и$arResult
. Автоматически вызывается при вызове$this->includeComponentTemplate()
$this->abortResultCache()
завершает кеширование, данные на диск не сохраняет. Метод на случай если кеширование запущено, но произошла ошибка и стоит прервать операцию. Если начать кеширование и не завершить его, появится множество багов$this->setResultCacheKeys($arResultCacheKeys)
принимает массив ключей по которому будет фильтрация данных в$arResult
. Позволяет кешировать не весь результат, а только нужные данные. При повторном вызове не затирает старые ключи, а дополняет их$this->getCacheID($additionalCacheID = false)
генерирует имя кеша. Если переопределить в своем классе и указывать$cachePath
, можно добиться того, что кеш не будет зависеть от текущего сайта$additionalCacheID
данные из вызоваstartResultCache()
$this->clearResultCache($additionalCacheID = false, $cachePath = false)
чистит кеш компонента, параметры аналогичныstartResultCache()
. Вместо вызова этой функции, пользуйтесь тегированным кешем$this->clearComponentCache($componentName, $siteId = "")
очищает кеш по имени компонента
Пример работы с кешем в компоненте при работе через class.php
:
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
class MyComponent extends CBitrixComponent
{
public function executeComponent()
{
// начало кеширования, если кеш уже существует - startResultCache() возвращает false, заполняет $arResult, выводит верстку
if ($this->startResultCache()) {
// если требуется отменить кеш
if (empty($this->arParams['KEY'])) {
// отменяем кеш
$this->AbortResultCache();
ShowError('Не установлен ключ');
return;
}
// логика по заполнению $this->arResult
$this->arResult['CITY'] = 'Ижевск';
// подключаем темплейт
$this->IncludeComponentTemplate();
}
}
}
Чтобы в настройках компонента появилась вкладка с настройками кеширования достаточно добавить параметр CACHE_TIME
:
<?php
$arComponentParameters = array(
"PARAMETERS" => array(
// параметры, которые к кешу отношения не имеют
"KEY" => array(
"NAME" => "API ключ",
"TYPE" => "STRING",
"GROUP" => "BASE",
"DEFAULT" => "",
),
// говорим что компонент работает с кешем
"CACHE_TIME" => array("DEFAULT" => "3600"),
),
);
При подключении в шаблоне других вложенных компонентов, указывайте вышестоящий через четвертый параметр IncludeComponent()
. В файле template.php
ссылка на вышестоящий компонент в переменной $component
. Иначе у вложенного компонента будут проблемы с подключением стилей, скриптов и component_epilog.php
.
$APPLICATION->IncludeComponent($componentName, $componentTemplate, $arParams, $parentComponent)
Если вложенный компонент использует кеширование, выключайте его в параметрах вызова. Во-первых вложенный кеш полностью бесполезен, во-вторых он вреден, так как теги вложенного компонента не установятся на актуальный кеш.
- В шаблоне компонента можно отменить запись кеша, а так же добавить теги на кеш
- Если вложенные компоненты не используют кеширование, их вывод всё равно будет закеширован в основном компоненте. После этого
result_modifier.php
иtemplate.php
вызываться не будут, работать будетcomponent_epilog.php
- Кеш зависит от параметров вызова компонента. Если в параметрах у вас динамические данные, то для каждого варианта будет свой кеш, в этом случае бывает выгоднее просто не использовать кеширование
- Если вы хотите взаимодействовать из шаблона иным способом, нежели вывод
HTML
, то делайте это через файлcomponent_epilog.php
. Например, не получится устанавливать заголовок страницы изtemplate.php
, этот файл 1 раз закешируется и заголовок устанавливаться больше не будет
Проблема чрезмерного роста кеша
Частая проблема на сайтах - огромный кеш меню, хотя в нем нет много данных. При работе компонента меню по умолчанию в кеш попадает в том числе выбранный пункт, который в свою очередь зависит от текущей страницы. Для этого компонент создает для каждой страницы отдельный кеш. Если страниц на сайте много, данные занимают большой объем на диске.
У компонента есть параметр CACHE_SELECTED_ITEMS
, который, по не понятной причине, не выведен в настройки компонента. По умолчанию он устанавливается в Y
и кеш начинает зависеть от адреса страницы. Чтобы отключить это поведение, добавьте такой параметр в вызов меню: "CACHE_SELECTED_ITEMS" => "N"
. В итоге получится у вас что-то такое:
<?php
$APPLICATION->IncludeComponent("bitrix:menu", "top", Array(
// тип меню для первого уровня
"ROOT_MENU_TYPE" => "top",
// тип кеширования
"MENU_CACHE_TYPE" => "A",
// время кеширования (сек.)
"MENU_CACHE_TIME" => "3600",
// учитывать права доступа
"MENU_CACHE_USE_GROUPS" => "N",
// не создавать кеш меню для каждой страницы
"CACHE_SELECTED_ITEMS" => "N",
),
false
);
Сброс кеша в Битрикс
Через админку
Основной способ сброса кеша находится в админке в разделе Настройки -> Настройки продукта -> Автокеширование
. Вкладка Очистка файлов кеша
. Варианты очистки:
Только устаревшие
удаление кеша срок годности которого уже истек. Удаляет файлы которые уже не используютсяВсе
всё понятно, удалить весь кешМеню
удалить кеш менюВесь управляемый
Все страницы HTML кеша
HTML кеш - это часть композитного кешаСайты24
удалить кеш лендингов сайтов 24
Удалить кеш кнопкой на эрмитаже
Нажатие кнопки на эрмитаже заставляет весь кеш на странице не видеть старые данные и создавать всё заново, после чего кеш сохраняется на диск. Важно понимать что кеш перезаписывается конкретно на вашем хите. Если у пользователей тот же кеш, то у них он тоже будет новый. А если кеш какого-то компонента зависит от групп пользователя, то у админов кеш сбросится, а у посетителей данные останутся старые.
Сбросить кеш програмно
Так же можно удалить кеш через API, но для этого надо знать как к нему обратиться.
Удалить кеш по тегу (в данном случае удалится кеш инфоблока 2):
$taggedCache = \Bitrix\Main\Application::getInstance()->getTaggedCache();
$taggedCache->clearByTag('iblock_id_2');
Удалить кеш зная имя и папку:
$cache = Bitrix\Main\Data\Cache::createInstance()
$cache->clean($uniqueString, $initDir);
Удалить весь кеш в папке:
$cache = Bitrix\Main\Data\Cache::createInstance()
$cache->cleanDir($initDir);
Другие варианты сброса кеша
Если кеш удаляется через админку долго, можно ему помочь удалив файлы на сервере через SSH
или FTP
. Удалите вручную все файлы в папках bitrix/cache
и bitrix/managed_cache
.
Кеш компонентов с автокешированием, которые используют инфоблоки, можно сбросить пересохранив какой-то элемент инфоблока (того инфоблока, который выводится в компоненте).
Кеширование в браузере
Браузер кеширует статические ресурсы (стили, скрипты, картинки...). Браузер не кеширует HTML страницу. (Кеширование HTML браузером можно сделать при работе через заголовок 304 Not Modified, но такое делают редко).
Если вы добавляете скрипты через штатное API Битрикс, например $APPLICATION->AddHeadScript()
, Bitrix\Main\Page\Asset::getInstance()->addCss()
, файлы script.js
, style.css
в шаблоне, то браузер будет сбрасывать кеш скриптов и стилей при изменении файла на сервере. Так происходит потому что Битрикс добавляет к адресу файла временную метку.
При изменении файла меняется его дата обновления, временная метка в ссылке на файл меняется, для браузера это новый файл, браузер запрашивает файл с сервера.
К картинкам временная метка не добавляется, поэтому при их обновлении могут быть проблемы.
Старый API кеширования
Использовать уже не стоит эти старые классы, есть удобные новые. Описываю для пониманию работы старого кода. Вместо единого нового класса есть 2 старых: CPageCache
- для кеширования только HTML и CPHPCache
- для кеширования HTML и переменных.
Получение экземпляра класса CPHPCache
$obCache = new CPHPCache;
Методы CPHPCache
$obCache->InitCache($TTL, $uniq_str, $initdir=false, $basedir = "cache")
проверяет наличие кеша на диске, инициализирует параметры. Переданные сюда параметры можно уже не передавать вStartDataCache()
-
$obCache->StartDataCache($TTL=false, $uniq_str=false, $initdir=false, $vars=Array(), $basedir = "cache")
$TTL
время жизни кеша$uniq_str
имя$initdir
папка относительно$basedir
$vars
переменные, которые в будущем могут сохраниться в кеш$basedir
папка относительно/bitrix
, лучше не трогать
$obCache->EndDataCache($vars=false)
завершает кеширование. Сохраняет HTML вывод, который был после начала кеширования, и переменные из$vars
на диск$obCache->GetVars()
получает сохраненные переменные из кеша$obCache->Output()
выводит сохраненный в кеше HTML$obCache->AbortDataCache()
отменяет начатое кеширование
Пример работы с классом:
<?php
// создаем объект
$obCache = new CPHPCache;
// время кеширования - 30 минут
$life_time = 30 * 60;
// формируем идентификатор кеша в зависимости от всех параметров которые могут повлиять на результирующий HTML
$cache_id = $ELEMENT_ID . $USER->GetUserGroupString();
// проверяем наличие кеша на диске
if ($obCache->InitCache($life_time, $cache_id, "/")) {
// получаем закешированные переменные
$vars = $obCache->GetVars();
// выводим на экран содержимое кеша
$obCache->Output();
}
// если кеша нет
else if ($obCache->StartDataCache()) {
// получаем некие данные
$vars = [];
// выводим сообщение
echo '<b>Какие-то данные, которые попадут в кеш</b>';
// завершаем кеширование
$obCache->EndDataCache($vars);
}
В CPageCache
всё аналогично классу CPHPCache
, только нет работы с переменными.
Получение экземпляра класса CPageCache
$obCache = new CPageCache;
Методы класса
$obCache->InitCache($TTL, $uniq_str, $initdir = false, $basedir = "cache")
$obCache->StartDataCache($TTL, $uniq_str=false, $initdir=false, $basedir = "cache")
$obCache->EndDataCache()
$obCache->Output()
$obCache->AbortDataCache()