Кеширование или как много хорошего в этом слове


Думаю не для многих разработчиков есть секретом что в ЛС встроен механизм кеширования информации, полученной от БД. Но мои частые наблюдения сообщают о другом — я не могу вспомнить сторонние плагины, которые использовали бы кеширование в своей работе. Да что лукавить — я сам его обходил стороной часто. Поэтому этот топик призван пролить луч света на использование кеширования в ЛС в своих плагинах, т.к. это довольно просто.

Итак, главное в вашем плагине определить точки где вы получаете данные и где их записываете/модифицируете. Я понимаю что это несложно — в маппере, но все же.
Кеширование должно работать по логике: пока данные не менялись — тащим их с закромов, а не выколачиваем из БД.

Потому придем к обобщенным примерам (они упрощенные):

1. Там где у вас идет изменение данных в БД (insert, update, delete) — чистим то, что сохранили в кеше т.к.
2. Там где идет выборка данных (select) — проверяем:
  • Есть ли данные которые хотим получить в кеше? Если есть — то возвращаем из кеша
  • Есть ли данных нет, то нужно их получить путем запроса к БД и то что получили тут же упрятать в каморку в кеш


Вроде как логично? Да. Теперь более подробно и с деталями. У модуля кеша ЛС есть интересные функции:
  1. Получение данных из кеша по имени, геттер
  2. Занесение данных в кеш по имени, сеттер
  3. Очистка кеша по имени, тегам

Имя — некое символическое представление того что вы ищете в БД. Например, вы хотели бы получить список книг от автора с ИД=2, вы бы выполнили запрос в БД где указали условие:

SELECT * from `books` WHERE `author_id` = 2

Вот и в кеше можно было сохранить результат выборки под именем, например, «book_author_2», а потом пока данные не меняются — таскать данные не из БД, а сохраненного значения.

Вот теперь собственно и сам код:
1. Когда делаем выборку (select) — ищем информацию в кеше по имени, например, «book_author_2», есть — возвращаем её, нету — берем из БД, сохраняем в кеш. И потом её возвращаем:

if (false === ($data = $this -> Cache_Get ("book_author_{$iAuthorId}"))) {
  $data = $this -> oMapper -> GetBooksByAuthorId ($iAuthorId);
  $this -> Cache_Set ($data, "book_author_{$iAuthorId}", array ("book_change_{$iAuthorId}"), 60*60*24*3);
}

2. Когда делаем изменения в БД (insert, update), чистим кеш по тегам:

$this -> Cache_Clean (Zend_Cache::CLEANING_MODE_MATCHING_TAG, array ('book_change', "book_change_{$oBooks -> getId ()}"));

3. Когда удаляем данные из таблицы — удаляем кешированный результат, который может содержать значение которое мы удаляем:

$this -> Cache_Clean (Zend_Cache::CLEANING_MODE_MATCHING_TAG, array ('book_change', "book_change_{$oBooks -> getId ()}"));


Я не сказал ещё о важном параметре — тегах кеша. Именно их мы видим в сеттере 3 параметром и при удалении данных. Т.е. при занесении информации нужно указать к чему эта информация относится, например, к получению лучших авторов книг на сайте. И если удалять автора, то удаление затронет выборку лучших авторов, поэтому при удалении в тегах можно указать и тег получения лучших авторов — это кеш будет сброшен и заново установлен при следующей выборке.
В сеттере видим последний параметр — это время действия кеша по-умолчанию. Т.е. если данные не изменятся 3 суток, то кеш потом все равно будет сброшен автоматически.

Код кеширования обычно ложится в модули плагинов.

Надеюсь эта статья поможет авторам плагинов для ЛС сделать их более быстродействующими.

Ну и напоследок хотелось бы сказать — лучшая документация по ЛС — это его исходники. Изучайте.

З.Ы. Использование кеша необходимо только там где в этом есть целенаправленная необходимость — мало меняющиеся данные, которые очень часто получают из БД. Если данные подвергаются постоянной смене, но использование кеша сомнительно.

Старый кеш (тот у которого вышло время) удаляется в ЛС постепенно. Кому интересно — engine/modules/cache/Cache.class.php, с 95 строки :)

UPD: Забыл добавить — удаление кеша по имени:

$this -> Cache_Delete ("book_change_{$oBooks -> getId ()}");

спасибо legioner

17 комментариев

avatar
avatar
Для дальнейшего изучения
avatar
по моему, все наши плагины используют кеширование, там где это нужно :)
sitemap, l10n, etc
avatar
ваши — да. но вот большая часть других плагинов — обходит эту ситуацию с кешированием.
avatar
Если использовать в плагине ORM, то кеширование будет отрабатываться автоматически — кешируются запросы возвращающие списки объектов
  • ort
  • +3
avatar
Где бы ещё документацию найти по ORM в LS…
avatar
Отличная статья. Недавно тоже стал применять кеширование и это делал по аналогии с Плагинами которые использовали cache, появилось несколько вопросов…

Я допустим делаю запрос в DB и записываю данные в cache
$tag = "advert_by_user_id_{$sUserId}_{$iPage}_{$iPerPage}";
if (false === ($data = $this->Cache_Get( $tag ))) {				
	$data = array('collection'=>$this->oMapper->getAdsByUserId($iCount,$iPage,$iPerPage,$sUserId),'count'=>$iCount);	
	$this->Cache_Set( $data, $tag, array("advert_update","advert_new"), 60*60*24*3 );
}
и что значит в этом случае array(«advert_update»,«advert_new») и если не правильно то как должно быть..?

Следующий отрезок кода по идее должен чистить Cache

# чистим зависимые кеши
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array('advert_update',"advert_update_{$oAdvert->getAdsId()}"));

но если я записывал переменные как advert_{$oAdvert->getAdsId()} то и чистить должен их как 'advert',«advert_{$oAdvert->getAdsId()}» а не так как показано на примере.
avatar
что значит в этом случае array(«advert_update»,«advert_new»)
Это теги. теги по которым позже можно легко почистить кеш. А переменная $tag — имя того что вы хотите занести в кеш.

вот этого уже хватит для очистки кеша. т.е. удаляется весь кеш, который содержит указанны тег

$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array('advert_update'));

сдесь важно понять что удаление не по имени, а по тегам.

но если я записывал переменные как advert_{$oAdvert->getAdsId()} то и чистить должен их как 'advert',«advert_{$oAdvert->getAdsId()}» а не так как показано на примере. 

я не совсем понял.
это будет имя для кеша:

advert_{$oAdvert->getAdsId()}

это будет тег для кеша (допустим, один):

advert

очистить весь кеш, который был занесен с указанием тега «advert»:

$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array('advert'));
avatar
PSNet спасибо за разъяснение, уже становится понятнее…
То есть мне лучше всего сделать так…
$tag = "advert_{$oAdvert->getAdsId()}";
if (false === ($data = $this->Cache_Get( $tag ))) {                             
        $data = array('collection'=>$this->oMapper->getAdsByUserId($iCount,$iPage,$iPerPage,$sUserId),'count'=>$iCount);        
        $this->Cache_Set( $data, $tag, array("advert"), 60*60*24*3 );
}

получается $tag = «advert_{$oAdvert->getAdsId()}»; == «advert_{$oAdvert->getAdsId()}» в array и второй раз мне это не нужно писать и просто сделать array(«advert»)..?
А чистить я получается смогу или всё с тегом «advert»
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array('advert'));
или только то значение которое мне нужно «advert_{$oAdvert->getAdsId()}»
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array("advert_{$oAdvert->getAdsId()}"));

Или опять я чего то не учёл..?
avatar
Да действительно, advert_{$oAdvert->getAdsId()} это будет имя для кеша, а advert это тег по которому будет чиститься кешь.
А как тогда сделать что бы можно было при обновлении не очищать все переменные с тегом advert а очистить или удалить только кешь где тег advert_{$oAdvert->getAdsId()}
avatar
теги разные ставить. а вообще советую таки заглянуть в модуль кеша ЛС — станет более понятно
avatar
PSNet спасибо большое за помощь, теперь понял как это всё работает и действительно помогло то что заглянул в модуль топиков кеша ЛС единственное я непониманию почему автор не указал функцию
$this->Cache_Delete("advert_{$oAdvert->getAdsId()}");
эта очень важная функция которая позволяет удалить кеш через имя, это очень полезно если используется GetModulsByArrayId и GetModulsAdditionalData

Когда всё сделал по аналогии с топиками LS и применил функции GetModulsByArrayId и GetModulsAdditionalData, плюс кешироваться стало всё стабильно, работоспособность выросла в разы…
avatar
забыл. спасибо. добавлю
avatar
Подскажите где можно посмотреть файлы сформированного кеша? У меня не работает кеширование. Мне кажется что возможно не указаны права на запись в нужном месте. Результаты статистики всегда одни и теже:

MySql
query: 255
time: 0,566

Cache
query: 0
— set: 0
— get: 0
time: 0
avatar
кеширование активируется в конфиге, хотя по дефолту оно ON
если бы были проблемы с правами — был бы ворох ошибок
avatar
болше похоже на рекурсивное обращение к БД…
avatar
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.