Memcached: статистика, отладка и RPC

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

Сегодняшний пост завершает эту серию, в нём обзорно мы поговорим о технических "мелочах":

  • анализ статистики memcached;
  • отладка memcached;
  • "RPC" с помощью memcached.

Полный текст всех разделов в виде одной большой PDF-ки можно скачать и посмотреть здесь (в разделе "Материалы").

Статистика работы memcached

Кроме необходимости реализовать механизмы работы с memcached, необходимо постоянно заниматься мониторингом кластера memcached-серверов, чтобы быть уверенным, что мы достигли оптимальной производительности. Memcached предоставляет набор команд для получения информации о его работе.

Самая простая команда, stats, позволяет получить элементарную статистику: время работы сервера (uptime), объем используемой памяти, количество get запросов и количество хитов (hits), т.е. попаданий в кэш. Их соотношение позволяет нам судить об эффективности кэширования в целом, хотя необходимо учитывать, что в memcached ключами являются не только закэшированные выборки, но и счетчики, блокировки, тэги и т.п., так что для вычисления чистой эффективности кэширования это значение требует корректировки. Из общей статистики мы также можем узнать, сколько ключей было удалено раньше истечения срока жизни (evictions), данный параметр может сигнализировать о недостаточности объема памяти memcached.

Slab-аллокатор

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

Механизм его работы заключается в том, что вся доступная memcached память делится на slab’ы (блоки), каждый из которых будет хранить элементы определенного размера. Например, slab для хранения объектов размером 256 байт, при этом сам slab имеет размер 1 Мб, таким образом он может сохранить 4096 таких объектов. Память внутри такого slab’а выделяется только по 256 байт. Если у нас есть slab’ы для объектов размером 64, 128, 256, 1024 и 2048 байт, то максимальный размер объекта, который мы можем сохранить – 2048 байт (в последнем slabе). Если мы хотим сохранить объект размером 65 байт, под него будет выделена память в slab’е-128, 1 байт – в slab’е 64.

Чтобы добиться эффективного использования памяти memcached для хранения наших ключей и значений, мы должны быть уверены в правильном выборе размеров slab’ов, который выделил memcached, а также в их разумном наполнении. Для этого мы можем попросить memcached предоставить статистику по slab’ам, которую можно, например, визуализировать в виде такого графика:

Статистика slabов memcached

Здесь на горизонтальной оси отложены размеры slab’ов, а на вертикальной – объем памяти, используемый slab’ами данного размера. В данный момент вся память memcached занята ключами и их значениями, поэтому данный график представляет собой текущее распределение значений в памяти сервера. Легко видеть, что больше всего slab’ов выделено под ключи с относительно небольшими значениями – до 20 Кб, для больших по размеру ключей slab’ов гораздо меньше. Такое распределение адекватно нашей задаче: у нас больше всего именно маленьких ключей (счетчики, блокировки, небольшие кэши). При этом эти же ключи занимают и бóльшую часть памяти, с локальными пиками выделения под ключи размером 300 байт, 8 Кб. Если график отличается от того, который ожидается по логике задачи, это повод для беспокойства.

Отладка проектов, использующих memcached

Мы написали большую подсистему для работы с memcached, реализовали различные механизмы решения проблем, связанных с высокой нагрузкой. Как проверить, что всё действительно работает так, как нам бы этого хотелось? Высокую нагрузку, сетевые задержки и т.п. практически невозможно воспроизвести в локальном окружении, непросто это сделать и в тестовом окружении. На серверах в production нам доступны лишь те механизмы отладки, которые не затрагивают нормальное функционирование самого приложения. Способ отладки не должен вносить ощутимых временных задержек, иначе он изменит поведение приложения, и отладка станет бессмысленной.

Можно предложить следующий «трюк», который может помочь в данной ситуации: для каждого кэша (ключа в memcached) или для группы кэшей (ключей) мы заводим отдельный файл в локальной файловой системе. В этот файл в режиме append мы дописываем по одному символу в ответ на каждое логическое действие, которое произошло с кэшом. Для просмотра в реальном времени поведения кэширующей подсистемы достаточно сделать tail –f на этот файл:

MLWUHHHHHHHHHHHHHHHMLLHHHHHHHHHH

Пусть буквы имеют следующий смысл:

  • M – кэш устарел (или не найден);
  • L – попытка заблокироваться;
  • W – запись (и построение) нового кэша;
  • U – удаление блокировки;
  • H – успешный запрос кэша.

Тогда по приведенной последовательности можно рассказать то, что происходило с данным кэшом: вначале он отсутствовал, мы кэш не обнаружили (M), попытались заблокироваться (L) для его построения, заблокировались, построили кэш (W), сняли блокировку (U), затем какое-то время кэш успешно работал, отдавая закэшированные данные (H). Потом в какой-то момент кэш устарел или был сброшен (M), мы попытались заблокироваться, не получилось (L), попытались еще раз (L), блокировка оказалась снята, кто-то другой построил новый кэш, мы его прочитали (H) и дальше им пользовались.

Межпроцессное взаимодействие с помощью memcached

Сложный проект состоит из отдельных компонент, сервисов, которые должны взаимодействовать друг с другом, используя механизмы RPC, вызовы API, обмениваясь информацией через БД или каким-то еще способом. Иногда для такого обмена информацией можно использовать и memcached.

В качестве примера рассмотрим сервис пользовательских вещаний: существует какое-то количество вещаний, в каждом из которых в данный момент времени находится некоторое количество зрителей. Популярность вещания определяется количеством зрителей. Актуальной информацией о количестве зрителей обладает только сервер вещаний, а список вещаний на странице вещаний формирует frontend. Конечно, можно было бы сделать так, чтобы сервер вещаний периодически сбрасывал в БД или через API в frontend информацию о количестве зрителей, или frontend мог бы через API сервера вещаний получать актуальную информацию. Однако количество зрителей – очень быстро меняющаяся характеристика, и в данной ситуации можно просто из сервера вещаний периодически (раз в несколько секунд) сохранять в memcached информацию о количестве зрителей в каждом из вещаний, а frontend, обращаясь к memcached, может получить информацию в любой удобный момент. Таким может быть межпроцессное взаимодействие, реализованное с помощью memcached.

Comments

Comments powered by Disqus
Contents © 2015 Andrey - Powered by Nikola