Как стать автором
Обновить

Memcached и метки. Реализация для фреймворка Kohana

Kohana
Приветствую всех.

Уже довольно давно разрабатываем проект на кохане и встала необходимость эффективного кэширования данных. Не то, чтобы вопрос производительности стоит очень остро в данный момент, но, хотелось бы подготовится заранее, а не писать систему кэширования в ночь после волны посетителей. Да и посещаемость постоянно растет, а в некоторые моменты бывают всплески до 3х раз по сравнению с обычным днем.

Собственно, в выборе системы кэширования особых вопросов не было — на ум сразу приходит всем известный memcached.

Если вы хотя бы немного смотрели memcached, то должны были отметить, что, по большому счету, он поддерживает только 2 операции: получение значения, запись значения. Нет никакой возможности вытянуть все ключи по определенному признаку или паттерну. Сделано это сознательно, с целью сделать его максимально простым, а значит, максимально быстрым.

Итак, представим ситуацию, что мы храним в кэше данные записей блога под ключами post_. В определенный момент нам требуется сбросить все записи с постами, при этом не очищая кэш полностью (там могут хранится другие данные, значения которых все еще актуальны). Ситуация получается безвыходная. Мы не знаем список ключей постов, получить их нет возможности, сбросить ключи по паттерну тоже невозможно. Что же делать?


Я не буду вдаваться в теоретическое описание способов, как на базе memcached можно реализовать систему тэгирования записей, по этому поводу и так есть много статей. Например, в блоге Андрея Смирнова: www.smira.ru/2008/10/29/web-caching-memcached-5. Именно этот способ я и использовал при создание системы кэширования на своем сайте.

Чтобы вы понимали о чем речь, мы собираемся хранить данные нашего кэша в массиве вида:

array (
'tags' => array (
'tag1' => <...значение...>,
'tag2' => <...значение...>
),
'data' => <....хранимые данные...>,
);

* This source code was highlighted with Source Code Highlighter.

Также, каждая метка у нас хранится в memcached с ключем tag_<имя тэга>. В значении мы храним некое число. В нашем случае, для надежности, мы храним в нем время создания/обновления метки с точностью до миллисекунд.
При добавлении нового элемента в кэш мы считываем это число и сохраняем его в том массиве, который описан выше. При считывании значения ключа мы проверяем, не изменилось ли значение метки относительно хранимых в значении ключа значений. В случае, если они различаются — мы считаем, что ключ сброшен. Таким образом, сброс ключей по метке производится путем изменения значения у ключа метки.

Например:
// значение post_1:
array (
'tags' => array (
'posts' => 1
),
'data' => 'post content'
)
// значение tag_posts:
array (
'data' => 1
)


* This source code was highlighted with Source Code Highlighter.


При считывании ключа post_1 алгоритм также считывает значение tag_posts и сравнивает его со значением, которое хранится в ['tags']['posts'] ключа post_1. В случае, если они равны — все в порядке, post_1 валиден. В случае, если значения отличаются — мы считаем, что post_1 не валиден и возвращаем NULL. Таким образом, мы можем помечать ключи не валидными просто изменив значения, которое хранится в «метках».

Если я объяснил не очень понятно — не беда. Сверху я давал ссылку на статью, где используемая мною методика описана более подробно.

Драйвер memcached в Kohana по умолчанию не поддерживает метки в memcached. Точнее, пару версий назад оно было реализовано, но работало оно так, что в итоге авторы решили отказаться от меток вообще. В кратце — там создавался один ключ в memcached, который хранил массив вида <название ключа> => <список меток>. Вы можете представить, какой размер был у этого массива на мало-мальски крупном сайте.

Скачать мою версию драйвера вы можете тут: github.com/Kolger/kohana-memcacheimp
Это обычный кохана-модуль. Скачиваете код, создаете директорию memcachedimp в папке application/modules вашего проекта. Не забудьте подключить модуль в config.php:

$config['modules'] = array
(
//.....
MODPATH.'memcacheimp', // Memcacheimp driver
//.....
);
// а также, выбрать драйвер в cache.php:

$config['default'] = array
(
'driver' => 'memcacheimp',
'lifetime' => 3600,
'requests' => 1000,
);


* This source code was highlighted with Source Code Highlighter.


Теперь, класс Cache будет работать с новым драйвером.
Использование ничем не отличается от способа, описанного в документации: docs.kohanaphp.com/libraries/cache
$cache = new Cache();
// Добавление в кэш:
$cache->set('post_1', 'post content', array('posts'));
$cache->set('post_2', 'post content', array('posts'));
// Получаем значение
$value = $cache->get('post_1');
// $value == post content
// "Удаляем" метку. А точнее, изменяем значение, которое хранится в ключе tag_posts, из за чего ключ post_1 и post_2 - сбрасываются
$cache->delete_tag('posts');
// Пробуем получить значение ключа post_1
$value = $cache->get('post_1');
// $value === NULL, чтд. На самом деле в значении ключ post_1 все еще хранится значение, но оно считается устаревшим, т.к. значение метки не совпадает с текущим.

* This source code was highlighted with Source Code Highlighter.


Думаю, немного изменив мой класс можно его использовать не только в Кохане, но и в любом другом проекте на PHP. Если есть идеи как улучшить, или оптимизировать класс — милости просим на гитхаб github.com/Kolger/kohana-memcacheimp :)

Что стоит усовершенствовать:
— Получение тэгов в методе get с помощью getMulti, чтобы не плодить запросы к memcached.
— Удаление ключей в случае, если метка изменилась.
Теги:memcachedphpkohana
Хабы: Kohana
Всего голосов 25: ↑20 и ↓5+15
Просмотры2.9K
Комментарии Комментарии 19

Похожие публикации

Лучшие публикации за сутки