Использование кэширования. Memcached в действии

Я уже публиковал небольшую замету о том, как установить демон Memcached на локальной машине под управлением ОС Windows. Судя по комментариям (еще в старом блоге), информация оказалась достаточно актуальной, и я решил написать еще одну заметку. На этот раз я постараюсь описать базовые принципы кэширования на примере работы с Memcached.

Для эффективного использования кэширующего механизма, необходимо хотя бы немного понимать принципы его работы. Это позволит предупредить возникновение весьма распространенных вопросов.

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




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

Так же следует заметить, что Memcached использует политику LRU-1. То есть, если отведенный демону объем оперативной памяти исчерпан, из кэша автоматически будут удалены те ключи, последнее время обращения к которым значительно отличается от текущего. При этом никак не учитывается частота обращения или объем закрепленных за ключом данных. Получается, что если ключ используется очень часто, но по каким-то причинам время последнего обращения к нему значительно старше, чем у других ключей, он все равно будет первым кандидатом на вылет.

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

Операции над кэшем

Существует всего пять широко востребованных операции над хранимыми данными:

  • установка данных в кэш по ключу
  • получение данных из кэша по ключу
  • обновление данных по ключу (перезапись)
  • удаление данных по ключу
  • декремент/инкремент числового значения в кэше

Также существует возможность «приписать» или «дописать» данные в уже имеющийся кэш (prepend/append) и другие, как мне кажется, малопригодные действия. В любом случае, пяти перечисленных выше будет более, чем достаточно для того, чтобы организовать адекватнеое кэширование наших данных.

Давайте рассмотрим всю процедуру работы с Memcached на каком-нибудь более или менее живом примере.

Допустим, наш ресурс предлагает посетителям регистрацию. При регистрации, посетитель заполняет регистрационную анкету, указывая свои личные данные, например, имя или никнейм.

Как правило, авторизованный пользователь может видеть свое имя в каком-нибудь месте на странице (что-то типа «Привет, Вася»). Откуда мы берем эти данные при генерации страницы? Правильно, из базы данных. А зачем? Ведь эти данные крайне редко редактируются, но используются постоянно. Следовательно, у нас появляется веский повод для того, чтобы запрашивать их из какого-то более шустрого на отдачу хранилища, нежели реляционная база данных. Ко всему прочему, таким образом, мы снизим нагрузку на БД, лишив ее необходимости обрабатывать один лишний запрос.

И так, имеем следующий код:

// Создаем объект класса Memcache и выполняем соединение с сервером
$cache = new Memcache();
$cache->connect('127.0.0.1', 11211);

// Проверяем наличие ключа в кэше
$user_data = array();

if (!$user_data = $cache->get('user' . $user_id)) {
	// Если метод Memcache::get() вернул false, значит ключ не найден в таблице
	// следовательно, берем данные из базы
	$user_data = mysql_fetch_assoc(mysql_query('SELECT user_name, user_nickname FROM users WHERE id = ' . $user_id . ' LIMIT 1'));

	// Помещаем данные в кэш на 24 часа
	$cache->set('user' . $user_id, $user_data, MEMCACHE_COMPRESSED, 60*60*24);
}

Первые две строки (не считая комментарий) создают объект класса Memcache и выполняют соединение с сервером. Потом, мы пытаемся получить кэш по ключу, состоящему из префикса user и порядковому номеру пользователя. Если кэш найден, то массив $user_data хранит данные пользователя и блок if не выполняется, а следовательно не выполняется и запрос в БД. Если кэш по запрошенному ключу не обнаружен, то берем из базы данные, помещаем их все в тот же массив и попутно кладем в кэш, чтобы в ближайшие 24 часа брать их оттуда.

Учитывая вопросы, которые неоднократно задавались в предыдущей редакции этой заметки, сразу оговорюсь, что класс Memcache становится доступен после установки расширения Memcache для PHP. То есть подключать какой-то отдельный php файл с классом не требуется. Такого файла попросту не существует.

В скрипте, который редактирует пользовательский профиль добавляем вызов метода удаления кэша или замены данных ключа.

// Если обновление данных в БД успешно
if (mysql_query('UPDATE users SET user_name = "'. $user_name .'", user_nickname = "'. $user_nickname .'" WHERE id = '. $user_id .' LIMIT 1')) {
	// Удаляем текущий кэш профиля
	$cache->delete('user' . $user_id);
}

Теперь, при выполнении скрипта, который содержит код из первого листинга, кэш найден не будет и данные будут получены из БД, после чего попадут в кэш на следующие 24 часа.

В принципе, на этом внедрение кэширования можно считать оконченным. Осталось включить подобную логику во всех скриптах, где нам требуется кэшировать какие-либо данные.

Когда кэширование актуально?

Человек, который впервые вкручивает кэширование в тот или иной проект, может допустить массу ошибок просто потому, что будет пытаться положить в кэш то, чему там совсем не место. Давайте определим несколько случае, где использование кэша необходимо или наоборот — совсем неоправданно. Исходить будем из востребованности и объема данных.

Кэширование — нет

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

Кэширование — да

  • Очень актуально кэшировать счетчики, дабы не брать их каждый раз из базы данных. Например, пользователь прочитал статью, соответственно, увеличил количество ее просмотров на единицу. Тем не менее, для нас не является критичным, если на сайте какое-то время будет демонстрироваться устаревшее значение счетчика.
  • В кэше весьма логично хранить данные (их часть) зарегистрированных пользователей, так как обращение к этим данным происходит очень часто, а изменяются они крайне редко. Ко всему прочему, как правило, пользовательский профиль не слишком велик по объему.
  • В высоконагруженных системах очень хорошим решение является хранение данных сессий в кэше. Например, в PHP стандартный механизм сессий хранит данные на жестком диске, что при большой посещаемости вызовет дополнительную нагрузку на файловую систему.

Вообще, кэширование необходимо использовать там, где оно действительно необходимо. Описать все возможные ситуации все равно не получится. Главное — понимание проблемы, тогда варианты решения найдутся сразу.

В статье приведены куски кода, написанного на PHP. В прошлой статье я как раз писал как установить Memcached в связке с PHP, поэтому считайте данный материал логическим продолжением. Но это никак не обязывает вас использовать именно этот язык. Благо клиенты написаны практически под все популярные языки программирования и право выбора всегда остается за вами.

LRU-2
Есть еще политика LRU-2, при которой приоритет отдается частоте обращения к ключу. Фиксируются две последних временных точки обращения и чем меньше разница между ними (считается, что ключ востребован), тем у ключа больше права на жизнь.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *