memcached на пальцах

15 Июль 2009 10 Comments

Ранее уже была сделана публикация с обзором memcached. Давайте вернемся к данной теме и рассмотрим практику работы с memcached на примерах.

К сожалению у меня по прежнему не доходят руки активно заниматься блогом, но наконец-то появился появился первый человек, откликнувшийся на мое предложение стать гостевым автором данного блога. Его имя Владислав Клименко и именно он является автором данного поста, а я лишь выступаю в роли редактора. Может быть данный пример подтолкнет и других читателей поучаствовать в возвращении Insight IT к жизни.

С уважением,
Иван Блинков

Итак, пара слов о предмете разговора. memcached — это распределенная система кэширования объектов в оперативной памяти. Разрабатывается фирмой Danga Interactive (кстати, они являются авторами не только memcached, но и других интересных проектов). Но о них, возможно, в следующий раз. Обычно memcached используется приложениями для временного хранения данных, которые надо часто читать. Приложения не взаимодействуют (обычно) напрямую с сервером memcached, а работают при помощи клиентских библиотек. На настоящее время созданы библиотеки для многих языков программирования (а для некоторых еще и по нескольку альтернативных)  — полный список клиентских библиотек доступен на wiki проекта. В целом, данная схема похожа на работу с БД, знакомую многим разработчикам.

Будем рассматривать установку и использование memcached для Linux. Так же при рассмотрении примеров на PHP и обзоре кэширования сессий потребуются PHP и Apache. Возможно, их придется установить, но мы не будем заострять внимание на вопросах установки.

Сервер memcached

Давайте приступим к установке memcached. Практически во всех дистрибутивах Linux memcached можно установить из репозитариев. Если есть желание собрать самую свежую версию, то можно заглянуть на сайт разработчика (на момент написания этих строк последняя версия — 1.4.0).
Также, возможно, понадобится установить libevent. Последняя стабильная версия — 1.4.11

Собираем, устанавливаем и запускаем memcached в режиме вывода сообщений. Интересно же посмотреть, что с ним происходит:

memcached -vv

Процесс запускается и ждет подключений (по умолчанию на порту 11211). Серверная часть готова обрабатывать подключения клиентов и кэшировать полученные данные.

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

Клиенты memcached

Из всего многообразия клиентских библиотек рассмотрим две:

  • libmemcached (для Си);
  • PECL extension для PHP (построенный на базе предыдущей библиотеки).

Си

Библиотека libmemcached на данный момент активно развивается и представляется наиболее подходящим выбором при работе с Си и PHP. Также, в комплекте с самой клиентской библиотекой поставляются дополнительные утилиты для работы с memcached, позволяющие просматривать, устанавливать, удалять значения в кэше memcached. Кстати, удивляет, что набор утилит идет не с серверной частью, а с клиентской библиотекой.

Итак, приступим к установке libmemcached. На момент написания этих строк текущая версия libmemcached — 0.31. Компилируем, устанавливаем. Для начала, наслаждаемся чтением страниц man:

man libmemcached
man libmemcached_examples

C библиотекой поставляются описание несложных примеров использования. За более интересными же способами применения имеет смысл заглянуть в исходные тексты утилит, благо все идет вместе.

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

  • memstat — выдает информацию о сервере memcached
  • memcat — выдает значение по ключу
  • memrm — удаляет значение по ключу
  • memdump — выдает список ключей

Для начала посмотрим, что скажет сервер memcached, запущенный нами немного ранее в режиме выдачи сообщений. Запросим статистику сервера при помощи утилиты memstat:

memstat --servers localhost

 Listing 1 Server
 Server: localhost (11211)
 pid: 14534
  uptime: 1950
 time: 1247390264
 version: 1.4.0
 pointer_size: 32
 rusage_user: 0.0
 rusage_system: 0.0
 curr_items: 0
 total_items: 0
 bytes: 0
 curr_connections: 10
 total_connections: 11
 connection_structures: 11
 cmd_get: 0
 cmd_set: 0
 get_hits: 0
 get_misses: 0
 evictions: 0
 bytes_read: 0
 bytes_written: 0
 limit_maxbytes: 67108864
 threads: 5

Получили статистику — следовательно memcached функционирует и откликается на запросы.

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

memcached предоставляет следующий набор основных функций (их, конечно, больше, но здесь приведены основные):

  • set - занести в кэш пару ключ-значение
  • add - занести в кэш значение при условии, что значения с таким ключом в кэше еще нет
  • replace — обновляет кэш при условии, что значение с таким ключом в кэше уже есть
  • get — получает значение из кэша по указанному ключу
    • Пример программы на C

      Файл mc.c
      #include "stdio.h"
      #include "string.h"
      #include "memcached.h"
      
      int main( void )
      {
      	char *key = "key";
      	char *value = "value";
      	uint32_t flags = 0;
      	size_t length = 0;
      	char *value2 = NULL;
      	memcached_return rc;
      
      	// 1. создать структуру для работы с кэшем
      	memcached_st *memc = memcached_create(NULL);
      
      	// 2. указать сервер с которым будем работать
      	memcached_server_add(memc,"localhost",11211);
      
      	// 3. занести пару ключ-значение в кэш
      	rc = memcached_set(memc, key, strlen(key), value, strlen(value)+1, (time_t)0, flags);
      
      	if (rc == MEMCACHED_SUCCESS) {
      	} else {
      		// обработать ошибку
      	}
      
      	// 4. получить значение
      	value2 = memcached_get (memc, key, strlen(key),     & length, & flags, & rc);
      	if (rc == MEMCACHED_SUCCESS) {
      		printf("%s\n", value2);
      		free(value2);
      	} else {
      		// обработать ошибку
      	}
      
      	// 5. высвободить структуру
      	memcached_free(memc);
      	return 0;
      }
      

      Программа состоит из 5 основных операций и в особых комментариях не нуждается. Разве что можно отметить, что в пункте 2 можно добавлять много серверов, в случае использования распределенной системы.

      Компилируем, возможно придется явно указать пути к библиотекам:

      gcc -Wall -o mc mc.c -I/usr/local/include/libmemcached/ -lmemcached
      

      Запускаем:

      ./mc
       value
      

      Видим требуемое значение — должно быть, заработало!

      Для уточнения деталей, смотрим сообщения на сервере memcached:

      <32 new auto-negotiating client connection
      32: Client using the ascii protocol
      32 STORED
      32 sending key key
      >32 END
      <32 quit
      <32 connection closed.
      

      В данном примере представлены следующие события: подключение клиента, установка пары ключ-значение, чтение данных по ключу и отключение клиента.

      Посмотрим статистику на сервере:

      memstat --servers localhost
       Listing 1 Server
       Server: localhost (11211)
       pid: 14534
       uptime: 4659
       time: 1247392973
       version: 1.4.0
       pointer_size: 32
       rusage_user: 0.0
       rusage_system: 0.0
       curr_items: 1
       total_items: 1
       bytes: 58
       curr_connections: 10
       total_connections: 13
       connection_structures: 11
       cmd_get: 1
       cmd_set: 1
       get_hits: 1
       get_misses: 0
       evictions: 0
       bytes_read: 58
       bytes_written: 58
       limit_maxbytes: 67108864
       threads: 5
      

      Следующие две строчки показывают, что в кэше появилось значение:

      curr_items: 1
      total_items: 1
      

      Посмотрим на данное значение:

      memcat --servers localhost key
       value
      

      Итак, приложение, использующее memcached — готово.

      PHP

      Для начала установим PECL extension для PHP — memcached

      pecl install memcached

      На этом этапе возможно появление сообщения об ошибке вида:

      ERROR: 'phpize' failed

      Это означает, что не установлен пакет php-dev или его аналог. Устанавливаем его и можно пробовать снова:

      pecl install memcached
       install ok: channel://pecl.php.net/memcached-1.0.0
       You should add "extension=memcached.so" to php.ini
      

      Как нам и советуют, дописываем extension=memcached.so в php.ini и перезапускаем Apache.

      Смотрим информацию об используемом PHP:

      memcached support  enabled
      Version  1.0.0
      libmemcached version    0.31
      Session support    yes
      igbinary support   no
      

      Пример программы на PHP

      Можно смело использовать обращения к memcached из PHP. Как обычно, рассмотрим пример:

      <?php
      $m = new Memcached();
      
      $m->addServer('localhost', 11211);
      $m->set('phpkey', 'phpvalue');
      var_dump( $m->get('phpkey'));
      ?>
      

      Результат работы данного скрипта:

      string(8)  "phpvalue"
      

      Итак, PHP-приложение, использующее memcached — готово.

      Кэширование данных сессий

      Memcached можно использовать и как хранилище данных сессий для PHP. Такой подход часто используется в реальных приложениях. Давайте рассмотрим, что для этого надо сделать.

      Вносим изменения в php.ini

      ;session.save_handler = files
      session.save_handler = memcached
      
      ;session.save_path = /var/lib/php5
      session.save_path = localhost:11211
      

      Параметр session.save_handler указывает, что теперь данные будут храниться в memcached. Второй параметр — session.save_path указывает сервер memcached (их может быть указано несколько, через запятую) на котором будут сохранятся данные.

      Перезапускаем Apache — и готово!

      Теперь надо проверить, что теперь данные сессии реально хранятся не на диске, а в memcached.

      Рассмотрим работу несложного скрипта, заносящего что-нибудь в сессию:

      <?php
      
      session_start();
      $_SESSION['intval'] = 123;
      $_SESSION['strval'] = "qwe";
      ?>
      

      Запускаем скрипт, он заносит данные в сессию, после чего смотрим на кэш

      memdump --servers localhost
       key
       keyphp
       memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f
      

      Итак — к нашим знакомым по предыдущим примерам ключам, добавился ключ с характерным именем memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f.

      Хранение данных сессии перенесено в систему кэширования. Более подробную информацию по работе с memcached из PHP можно почитать на сайте PHP.

      Заключение

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

      10 comments

      • И все таки не советуется хранение сессий в мемкеше, т.к. нет гарантии что данные в нем сохранятся. Не приятно иногда разлогиниваться ;) . Как вариант — использовать memcacheDB. Те же яйца, только с гарантированным сохранением данных :)

      • А не могли бы вы рассказать про требуемые ресурсы? проц и оперативка? Буду очень благодарен =-)

      • Если приложение требует гарантии сохранности сессий — существует и другие варианты их хранения, скажем очень неплохим вариантом является использование реляционной СУБД.

        А требуемые ресурсы минимальны по сравнению с тем объемом памяти, которое выделяется под использование slab-аллокатором (т.е. под сам кэш). Ими можно смело пренебречь.

      • Владислав Клименко:

        Да, memcached не дает гарантий что при выполнении чтения данные всё ещё будут в кэше. Думаю, действительно следует сказать пару слов о memcacheDB, но это на будущее.

        Что касается памяти, выделяемой memcached, то желаемый объем можно явно указать в командной строке. В рассматриваемых примерах этого не было сделано просто за ненадобностью.

      • memcachedb на самом деле не настолько интересен, как это кажется на первый взгляд — у данного проекта существенно более узкая сфера применения, по сравнению с оригинальным memcached.

      • Rauan Maemirov:

        Для memached под Си советую использовать memcached_pool_create. Потому что если использовать его в демонах, у меня были загвоздки с клонированием «объектов» memcached. А так, мы просто создаем пул с нужным для нас количеством подключений, и берем подключение посредством memcached_pool_pop (и, соответственно, возвращаем через memcached_pool_push).

      • Владимир:

        А как memcached обрабатывает конкуррентные запросы по одному и тому же ключу? Блокировки или что там?

      • Насчет блокировок гарантированно можно сказать, наверное, только покопавшись в его исходниках, но на сколько я себе его представляю по собственному опыту: при операциях записи клиент асинхронно отправляет данные выбранному демону, а у того на входе просто что-то вроде очереди транзакций. Но могу ошибаться, так как копать детально данный вопрос у меня как-то не возникало желания; в целом на работу приложения-клиента механизм функционирования самого демона особо не влияет.

      • @Владимиру:
        Как мемкеш обрабатывает конкурентные запросы? Превосходно. Особенно отлично конкурентные запросы обрабатывает фейсбуковский мемкеш, переписанный на UDP. 300К запросов в секунду обрабатывает.

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

        А в многопоточном может быть есть блокировки на работу с аллокатором. Один клиент сделал set x foo. Другой сделал set x bar. Какое значение будет в x? Может быть одно, может быть другое. Какая вам разница? Клиентам разницы нет, они это показали, когда использовали безусловный set.

        @Ивану:
        Клиент (например, упомянутый в статье libmemcached) работает еще как синхронно, к сожалению, почти всегда. Для чего вы там упомянули асинхронного клиента — непонятно.

        Транзакции в мемкеше не нужны, т.к. все операции, включая CAS (check-and-set, про которую, почему-то умолчали) атомичные, то есть выполняются в один шаг.

      • Валерий:

        А вот все же любопытно. Если сервер один и нет надобности расшаривать сессии для нескольких серверов.
        Будет ли в этом случае у memcached какое то приимущество по сравнению с tmpfs или даже просто /dev/shm
        Почему то мне кажется, что нет.

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

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

      *

      Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>