Продолжая развивать тему кеширования, начатую в прошлом посте, хочу немного написать про тегированный кеш в битрикс. На самом деле технология нужная и правильная, правда ей почему-то редко кто пользуется.
Вкратце — как работает почти любая система кеширования:
Кеш — совокупность каких-то данных (структурированных или нет). Кеш имеет определенный период жизни и какой-то идентификатор (нечто, что уникально идентифицирует именно эту совокупность данных в каком-то контексте). Пока период жизни кеша не истек — нужно обращаться к кешу, а после истечения срока жизни кеша нужно его актуализировать и снова сохранить на указанный период.
Суть тегирования кеша в том, чтобы пометить какой-то кеш какой-то меткой и иметь возможность по тегу управлять этим кешем (в основном для очистки). Т.е. помимо идентификатора, кеш может быть идентифицирован и по тегу. Только тут есть разница. Тег — это не уникальный идентификатор (как правило). Можно привести сравнение с идентификаторами и классами в html — по спецификации на странице с одним id может быть только один элемент, тогда как одинаковых классов на странице может быть множество. Также и здесь — один и тот же кеш может иметь разные теги, а один и тот же тег может быть назначен разным кешам. Таким образом, имея какой-то тег, мы можем почистить все кеши, связанные с данным тегом.
Так как же использовать тегированный кеш? Все просто. Вся работа состоит из 3х операций:
- Cвязь объекта тегированного кеша с директорией (относительно /bitrix/cache), в которой хранится кеш, который вы хотите связать с тегом.
- Задание тега (или тегов)
- Очистка
Теперь технически. Перво наперво нужно включить механизм тегированного кеширования. Определяем в dbconn.php константу:
1 |
define("BX_COMP_MANAGED_CACHE", true); |
А теперь по пунктам рассмотрим пример кеширования из предыдущего поста с применением тегированного кеша. За работу кеша отвечает глобальный объект $CACHE_MANAGER, который является инстансом класса CCacheManager, поэтому его необходимо заранее забрать из $_GLOBALS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<? $cacheTime = 3600; $cacheId = 'myUniqueCacheId'; $cachePath = '/my/cache/path'; $obCache = new CPHPCache(); if ($obCache->InitCache($cacheTime, $cacheId, $cachePath)) { $arResult = $obCache->GetVars(); } else { //Достаем CACHE_MANAGER из глобалки и привязываем его к нашей директории кеша global $CACHE_MANAGER; $CACHE_MANAGER->StartTagCache($cachePath); //Вешаем тег на текущий кеш. У нас тут будет выборка элементов инфоблока каталога //поэтому имеет смысл зарегистрировать тег, зависящий от ID инфоблока каталога $CACHE_MANAGER->RegisterTag('iblock_id_' . CATALOG_IBLOCK_ID); //Делаем выборку из базы, например $dbProducts = \Bitrix\Iblock\ElementTable::query() ->addFilter('IBLOCK_ID', CATALOG_IBLOCK_ID) ->addSelect('NAME', 'DETAIL_TEXT', 'CODE', 'ID') ->exec(); while ($arProduct = $dbProducts->fetch()) { $arResult['PRODUCTS'][] = $arProduct; //Можно пометить кеш тегами всех товаров каталога $CACHE_MANAGER->RegisterTag('product_' . $arProduct['ID']); } //Можно повесить и какой-то другой более понятный тег с адекватным именем $CACHE_MANAGER->RegisterTag('catalog_iblock_id'); //Финализируем тегирование кеша $CACHE_MANAGER->EndTagCache(); //после чего сохраняем результат выборки в кеш if ($obCache->StartDataCache()) $obCache->EndDataCache(array("myDBResult" => $arResult)); } |
В результате работы данного кода мы закешируем список всех товаров нашего каталога. Сформированный кеш будет находиться в директории /bitrix/cache/my/cache/path, и этот кеш будет помечен тегами «iblock_id_1» и «catalog_iblock_id», а также набором тегов, зависящих от идентификатора товара, т.е. от ‘product_123’ например, до ‘product_N’.
Осталось самое простое — прикрутить возможность очистки данного кеша. Как известно — в битриксе широко развита система событий. В нашем примере в кеше находится весь список товаров, соответственно данный кеш устаревает сразу же при изменении любого товара. Соответственно, при изменении, удалении или добавлении какого-либо товара нам нужно данный кеш очищать. Для этого создаем обработчики для данного типа события и очищаем кеш по любому из ранее обозначенных тегов вот такой конструкцией:
1 2 3 4 |
global $CACHE_MANAGER; $CACHE_MANAGER->ClearByTag('iblock_id_1'); // или $CACHE_MANAGER->ClearByTag('catalog_iblock_id'); // или $CACHE_MANAGER->ClearByTag('product_' . $arProduct['ID']); |
Если вы хотите использовать тегированный кеш в своих компонентах, использующих автокеширование, то вам не нужно привязывать $CACHE_MANAGER к директории кеша компонента — это за вас сделает ядро. Внутри блока кеширования ($this->StartResultCache() {}) нужно только зарегистрировать нужные вам теги и следить за актуальностью данных в них.
Вот в общем-то и все, просто и удобно! С помощью него можно поддерживать кеш в актуальном состоянии при любом изменении данных.
Кстати говоря — в стандартной поставке тегированный кеш обеспечивает обновление кеша компонентов, использующих инфоблоки. Реализация крайне проста — в ядре внутри CIBlockResult::Fetch() и CIBlockResult::GetNext() (для элементов и разделов) спрятана регистрация тега вида ‘iblock_id_’ . $res[‘IBLOCK_ID’]. Соответственно, если вы используете в компоненте автокеширование, то для компонента, использующего эти конструкции будет зарегистрирован кеш с идентификатором инфоблока.
А уже внутри Add, Update и Delete методов для разделов и элементов спрятаны вызовы очистки кешей, связанных с тегом ‘iblock_id_’ . $res[‘IBLOCK_ID’]. Соответственно, даже если ваши компоненты закешированы на 100 лет, то при обновлении элементов, использующихся внутри этих компонентов кеш будет сброшен, и на сайте будут всегда актуальные данные.
Есть и краеугольный камень всей этой красоты — все теги хранятся в БД. Поэтому не стоит усердствовать с ними. Лучше делать теги, которые покрывают большой набор данных, чем делать теги, например, на каждый товар. Ведь при обновлении товара все равно придется чистить кеш всего компонента, как ни крути. Но это может иметь смысл для компонента детального просмотра товара, например, где кеш напрямую будет зависеть от конкретного товара.
В общем — решать вам. Инструмент — крутой, рекомендую!