From: rimas
Date: Mon, 1 Nov 2007 18:21:07 +0000 (UTC)
Subject: Использование в Perl системы распределенной памяти memcached
Оригинал: http://kiev.pm.org/?q=node/5
Предположим, Ваше приложение достаточно активно использует базу
данных. Сейчас это уже стало достаточно распространенным явлением, что
вполне реально встретить приложение уровня Блокнот-а, которое хранит
файл конфигурации или что-то еще в базе типа sqlite. Если же говорить
про более серьезные приложения, то базы данных там укоренились
достаточно прочно. В достаточно большом числе случаев именно БД
становится узким местом в системе, ее производительность уже не может
удовлетворить всех и приложение начинает притормаживать, а под большим
количеством пользователей и вовсе зажимает ручник потуже и замирает.
У вас есть служба, возвращающая состояние погоды в области согласно
запросу. Информация об изменении погоды поступает в систему
автоматически и сбрасывается в базу данных. 10 000 пользователей
более-менее одновременно захотят узнать погоду в одном регионе, в
результате чего будет сгенерировано и отправлено 10 000 запросов типа
SELECT something FROM somewhere WHERE id=MY_REGION_ID; Да, у базы
данных тоже есть кеш, и данные, после некоторого количества запросов
будут извлекаться именно оттуда, а не из файлов, но....
А вот если мы слегла модифицируем систему? Поток обновления информации
будет единожды заносить данные в таблицу и менять значение в локальном
кеше. На чем именно сделан этот кеш - не столь принципиально сейчас,
важно само его наличие. Взаимодействующую с пользователями часть тоже
нужно переписать таким образом, чтобы извлекать информацию изначально
из кеша и только в том случае обращаться к базе данных, если кеш
оказался пуст. В идеале для наших 10 000 поклонников погоды не будет
выполнено ни одного SQL запроса, что даст весьма ощутимый выигрыш в
быстродействии. Это производит очень сильное впечатление на конечного
владельца системы, проверено:) А конкурентам, использующим лишь базы
данных, остается лишь кусать локти и спешно осваивать новые
технологии.
Где еще можно применять кеши? Это и обмен данными между различными
процессами, как на одном компьютере, так и по сети, хранение настроек,
извлеченных из файла конфигурации, дабы сделать его доступным сразу
для всех и динамически изменяемым. Вариантов может быть масса, я
упомянул лишь о тех, с чем сталкивался сам.
В настоящий момент у программиста на Perl при работе с кешами может
возникнуть только одна проблема - проблема выбора. Существует большое
количество различных технологий и библиотек, нужно лишь знать, что,
как и для чего применять. Я расскажу о тех, чем доводилось
пользоваться мне. Сегодня речь пойдет о memcached.
Система распределенной памяти memcached(http://danga.com/memcached/)
была разработана как часть проекта livejournal.com для того,
чтобы минимизировать количество реальных запросов к базе данных
и представляет собой приложение, которое размещает в памяти и
извлекает по запросу определенные данные. Не знаю, существует ли
версия memcached под Windows, Linux же версия может быть
скомпилирована из исходных текстов или загружена уже готовая в
наиболее распространенных пакетах.
Запустим сервер в консоли:
memcached -l 127.0.0.1 -p 12345
Сам сервер имеет несколько ключей конфигурации, может принимать
входящие запросы как по TCP/IP, так и по UNIX Domain сокетам. С
помощью параметра -vv командной строки можно включить логирование на
консоль всех запросов, что очень помогает при отладке.
Несмотря на то, что сервер запускается только под достаточно
ограниченное количество операционных систем, клиентских API под него
более, чем достаточно(нет для ассемблера:), но существует для большого
числа наиболее распространенных языков программирования). Только под
Perl их существует что-то около 5 вариаций.
Исходя из принципов калмыцких певцов - что увидел, о том и пою -
расскажу о тех модулях, что использую сам. Cache::Memcached является
официальным модулем, поддерживаемым Danga, и, по большому счету, он
вполне справляется с большинством задач.
Давайте рассмотрим небольшой пример:
use Cache::Memcached;
my $cache = Cache::Memcached->new({servers => ["127.0.0.1:12345"], debug => 0});
#Заносим значение в кеш
$cache->set("key1", "some value");
#Читаем из кеша и печатаем результат на консоль
print $cache->get("key1");
По большому счету, этот нехитрый пример показал основные методы,
которыми приходится пользоваться в повседневной практике. Все
достаточно просто, но за этой простотой стоит высоконадежный и
быстродействующий сервер memcached. В моей практике он никогда не
"падал" и не зависал, так что могу рекомендовать его для применения
в серьезных приложениях.
Работа с кешом напоминает работу с ассоциативным массивом, где ключ
должен быть уникальным. Какие данные хранит в себе ключ не является
принципиальным. Существует возможность задать время жизни отдельного
ключа. Делается это с помощью дополнительного параметра метода set
$cache->set("key2", "my value", 10);
Время задается в секундах. При запросе через 11 секунд метод
get("key2") вернет пустую строку, так как значение устарело и будет
удалено из кеша.
Ключи не могут содержать в себе символов пробелов, чем это вызвано я
не знаю, могу лишь сказать, что попытка записать значение по ключу "my
key" закончится неудачей, правда, отследить это очень трудно.
Тут всплывает одна особенность memcached, которая лично мне не сильно
нравится - нет возможности отслеживать ошибки. Во время сеанса работы
у Вас может произойти та или иная ошибка, например кто-то может
остановить сервер memcached. Кроме того, у memcached, как и у каждого
сетевого сервера, есть определенные ограничения на количество
одновременных подключений. По умолчанию оно равно 1024 и может быть
расширено, однако если 1025 клиентов сделают запрос одновременно и
сервер не будет настроен на такое количество, кто-то останется без
обеда. Все бы ничего, но отследить эту ошибку, что Ваш запрос был
отвергнут, практически невозможно. Такое встречается очень редко, но
потенциально возможно. Предупрежден - значит вооружен.
Memcached можно использовать как общий генератор ID. Может, конечно
два метода incr и decr можно использовать и для других, более
прозаичных целей, но я об этом не знаю. Если есть идеи - поделитесь:)
#Заносим число в кеш
$cache->set("myID", 10);
#Увеличиваем счетчик на 1 и извлекаем новое значение
my $transaction_id = $cache->incr("myID");
#В $transaction_id будет находится число 11
Команда decr работает аналогично, только в сторону уменьшения.
Данные можно не только хранить, но и удалять из кеша. Для этого
существует метод delete:
#Удаляем запись из кеша
$cache->delete("key2");
Мы так же можем временно запретить занесение новых значений в кеш под
старым ключом.
#В течении последующих 10 секунда запись новых значений в ключ "key1" будет невозможна
$cache->delete("key1", 10);
Cache::Memcached обладает такой замечательной функцией, как
хранение/извлечение комплексных данных - массивов, хешей, объектов.
use Order;
my $order = Order->new();
$order->id(123);
$cache->set("my_order1", $order);
#Далее по тексту
my $order_1 = $cache->get("my_order1");
#Будет возвращено 123
print $order_1->id;
Можно обмениваться объектами по локально, так и по сети, модуль
Storable решает проблемы сериализации/десериализации совершенно
незаметно для пользователя. Следует отметить, что передача объектов
возможна лишь в пределах одной языковой среды perl <-> perl, например.
А вот прочесть объект, записанный из perl в приложении, созданном на
java уже не представляется возможным. Я думаю, что существует
возможность разбирать бинарные потоки сериализаторов различных языков,
но вот стоит ли оно усилий, которые нужно будет затратить?
В качестве решения для передачи объектов по сети можно применить
следующую конструкцию из ключей object_key_field_id, например
1_1
1_2
1_3
и т.д.
Если две стороны знают ключ объекта и нумерацию или названия полей,
восстановить объект будет не так и сложно.
Все имеет свои ограничения и memcached не является исключением.
Максимальный размер объекта, который можно записать в кеш составляет 1
MB. В принципе, этого более чем достаточно для подавляющего числа
задач, особенно если подходить к хранению объектов творчески. При
подсчете размера объекта, особенно комплексного, будет учитываться
даже размеры имен полей в хешах. У меня был случай, когда имена ключей
занимали больше места, чем сами данные. Решить эту проблему можно,
применяя те или иные константы, вынесенные в отдельный файл. Такой
подход дает значительную экономию по сравнению с прямым указанием
полей.
use constant FIELD_1 => 1;
....
#Размер ключа - 1 байт
$my_object->{FIELD_1} = "my value";
#Размер ключа - 7 байт
$my_object->{"field_2"} = "my another value";
Однако даже хитрое именование ключей не спасет, если значение само по
себе большое. Решить вопрос можно с помощью сжатия передаваемых
данных. Создатели модуля Cache::Mamcached позаботились об этом.
Конструктор new позволяет установить границу, при превышении которой
объект будет сжат
Таким образом, каждый объект, размер которого превышает 10 000 байт
будет сжат и передан по сети в сжатом виде. Его последующее извлечение
будет сопровождаться разархивацией.
Мои субъективные выводы о memcached:
PRO
* высокая скорость и надежность работы
* возможность использовать как в локальных, так и распределенных задачах
* наличие API под большое число языков программирования
* возможность работать с комплексными структурами данных
* библиотека Cache::Memcached позволяет сжимать передаваемую информацию
CONTRA
* нельзя отслеживать ошибки
* при аварийном останове кеш уничтожается(хотя это не столь важно для большинства задач)
* нет возможности получить список всех ключей системы
572 Прочтений • [Использование в Perl системы распределенной памяти memcached (perl memcached cache optimization hash)] [08.05.2012] [Комментариев: 0]