Кэширование SQL-запросов с Zend_Cache.
Из всех возможностей компонента Zend_Cache, предоставленных разработчиками фреймворка, я применял на практике в том числе и на этом сайте, только кэширование запросов к базе данных. Статистику запросов можно посмотреть в левом нижнем углу. Если на этой странице никого не было в течении получаса, при первой загрузке будет много запросов. На страницах категорий и рубрик сайта, в т.ч. и на главной, кэшируется все! и в лучшем случае не приходится даже подключаться к базе.
Что такое фронтэнд и бэкэнд своими словами или теория кэширования light.
Frontend - это средство хранения данных, которое позволяет получать их в разы быстрее, чем из другого хранилица. То самое другое хранилище и есть backend. К бэкэнду мы стараемся обращаться редко, только когда в этом есть необходимость, потомучто он тяжелый. Фронтэнд мы даем на разтерзание сотням и тысячам пользователей.
Короче бэкэндом можно считать БД, она у меня на другом сервере и туда не очень хочется постоянно слать запросы. Поэтому я записываю результат запроса в файл и при следующих вызовах скрипта, получаю информацию из него. Пока не пройдет заданное мной время - 30 минут, информация в файле считается актуальной. Или же приходится принудительно делать ее неактуальной, если обновились данные в базе.
Таким образом за эти пол часа пришли 5-6 пользователей и я сэкономил их время на 0.5 - 0.3 сек. и пожалел сервера (хоть они и американские), а вдруг когда нибудь придет 500-600 человек...
Хэлпер который инстанцирует объект.
Сначала был хэлпер, чтобы каждый раз настройки кэша брались из конфигов и это происходило в одном месте. Хелпер только создает объект как нам нужно и возвращает его для работы, так что этот момент можно реализовывать как угодно. У меня он выглядит таким образом:
<?php
require_once 'Zend/Controller/Action/Helper/Abstract.php';
class My_Helper_Action_Cache extends Zend_Controller_Action_Helper_Abstract
{
protected $cache;
public $config;
// Это происходит единожды, при первом вызове хэлпера
public function __construct()
{
$this->config = new Zend_Config_Ini('/some/path/config.ini', 'cache');
}
// При прямом вызове хэлпера у меня ничего не происходит
public function direct()
{
}
/**
* @var int
* @return obj Zend_Cache */
public function getCoreCache($life_time = null)
{
//Если $life_time не передана, то берем из конфига
if(!$life_time) $life_time = $this->config->life_time;
/* Если объект кэша еще не вызывался или нужно создать аналогичный объект, но с другими настройками
создаем! */
if (!$this->cache || $life_time != $this->cache->getOption('lifetime')) {
require_once 'Zend/Cache.php';
$front_opt = array(
// Вообще кэшировать ли? Полезно для отладки добавлять такую опцию в настройки
'caching' => $this->config->caching,
'lifetime' => $life_time,
/* Сериализует автоматически объекты и масивы перед записью в файл
вообще это работает медленней, но так гораздо удобнее */
'automatic_serialization' => true
);
$back_opt = array(
'cache_dir' => '/some/path//cache/',
// Вот тут нужно поэкспериментировать...
/** 'read_control_type' => 'adler32', @todo */
// Тут тоже
'hashed_directory_level' => '1',
// Права доступа к создаваемым файлам
'hashed_directory_umask' => '0766'
);
$this->cache = Zend_Cache::factory('Core', 'File', $front_opt, $back_opt);
}
return $this->cache;
}
}
Работа в контроллере и метки.
// Получить объект
$cache = $this->_helper->cache->getCoreCache();
// Если данные не закэшированы или истекло время
if (!$posts = $cache->load("zf_posts")) {
$posts = // запрашиваем из БД что нам нужно
// Закэшировать, как это просто!
$cache->save($posts, "zf_posts");
}
// Независимо от того как эти данные оказались в переменной, мы работаем с ними одинаково например:
print_r($posts);
Единственное, что хочется добавить к комментариям: zf_posts это метка, для того чтобы знать какие данные достать из каши фронтэнда. Все записываемое в кэш должно иметь уникальную метку, в противном случае данные перезатрутся и будут косяки.
Очистка кэша
Следующее что случится, это наступит момент, когда из кэша нужно что-то удалить. Делается так же просто как и записывается:
$cache = $this->_helper->cache->getCoreCache();
$cache->remove("zf_posts");
Самое главное следить за моментами, когда необходимо его очищать тут разработчики ZF не помогут. В основном это приходится делать только после редактирования или удаления.
Тэги.
Так же есть очень хороший способ группировать данные во фронтэнде используя тэги. Как я уже говорил данные должны иметь уникальную метку и если добавился комментарий к посту, нужно стереть из кэша все комментарии к посту (если хранились "пачкой"), и сам пост т.к. в нем содержится информация о колличестве комментариев, а оно изменилось.
Две единицы данных (пост и комментарии к нему), хранятся под разными метками и придется выполнить две операции удаления. Этого бы не случилось если бы изначально все делалось так:
$cache->save("post_12", array("post12"));
$cache->save("post_12_comments", array("post12"));
Сохранили данные под двумя разными метками, но с одним тэгом, и теперь удалить можно одной операцией, вместо 2-х 3-х 4-х и т.д.
$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('post12'));
Это все что я вычитал в мануале и попробовал на практике. Может что-то еще добавлю. Надеюсь вам будет от этого польза. Так же буду рад увидеть отзывы, критику и все такое.
Комментарии
Спасибо Анжелика. А Вам успехов в вашем начинании!
Здравствуйте, djaarf. Спасибо Вам за статьи. Я еще только учусь использовать данный framework и считаю Ваши статьи очень полезными. Продолжайте свой нелегкий труд и успехов Вам!