API взаимодействия с memcached представлен во всех популярных языках, поэтому в задачах кэширования это наиболее используемый инструмент. В случае когда не требуется ничего кроме кэширования, видимо, — самый оправданный.
Одна из проблем с которой я столкнулся при работе с memcached — невозможность сбросить его состояние на диск. Существующие решения либо не являлись простым кэшом (представляя скорее БД), либо не были настолько же стабильны и поддерживаемы. Вдобавок имелось желание «покодить», поэтому какой-то из готовых вариантов я мог банально упустить.
Зачем это может понадобиться
Причина почему обычно не нужен персистентный кэш — данные легко заполучить снова. Однако, если есть вероятность того, что в «час пик» кэш-сервера с hitrate=60% перезагрузятся, а сервис физически не сможет справиться с возросшей нагрузкой, то эту вероятность стоит учесть.
Форк и выполнение дампа
Представленный форк, memcached-dd, решает именно обозначенную проблему — производит дамп записей на диск. Использование простое — достаточно запустить модифицированный memcached с указанием имени файла в параметре -F:
memcached -F /tmp/memcache.dump -m 64 -p 11211 -l 127.0.0.1
При старте файл /tmp/memcache.dump будет загружен (если он существует). Туда же будет произведена и запись нового дампа. Управление распорядком записи дампов и т.п. предполагается извне, инициирование — сигналом SIGUSR2. Стоит отметить, что дамп сперва запишется файл .tmp и лишь затем, при успешной записи, будет переименован.
Особенности выполнения дампа
- Дамп производится в отдельном потоке и не блокирует работу memcached
- Если используется TTL, то при загрузке дампа TTL будет равен оному на момент выгрузки
- Просроченные данные не попадают в дамп (*)
Пример использования
Посмотрим как использовать memcached с Perl-скриптом, загружающим в него «автогенеренные» строки
use Cache::Memcached;
$memd = new Cache::Memcached {
'servers' => [ "127.0.0.1:11211" ]
};
$| = 1;
for (my $i = 0; $i <= 10000; $i++) {
$memd->set( "key_$i", "x"x100 . " [$i]" );
# my $val = $memd->get( "xkey_$i");
if ($i % 1000 == 0) {
print "r$i...";
}
}
Используем этот скрипт после запуска memcached
$ ./memcached -P /tmp/memcached.pid -F /tmp/memcached.dump -m 128 -p 11211 -l 127.0.0.1
# загрузим данные
$ perl ./load.pl
# произведем дамп
$ kill -USR2 `cat /tmp/memcached.pid`
1Mb dumped: 10001 items (0 expired during dump, 0 nuked by flush)
Moving temprorary /tmp/memcached.dump.tmp -> /tmp/memcached.dump
Итак, у нас есть файл /tmp/memcached.dump с 10001 записями. Перезапустим memcached с тем же флагом -F и проверим на существование ключа #1:
$ ./memcached -P /tmp/memcached.pid -F /tmp/memcached.dump -m 128 -p 11211 -l 127.0.0.1
$ echo get key_1 | nc 127.0.0.1 11211
VALUE key_1 0 104
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx [1]
END
Как видно, содержимое успешно восстановилось из дампа.
Это решение позволяет задействовать имеющееся API memcache, т.е. использовать дамп не модифицируя схему общения. Надеюсь, что такая возможность будет полезна при особых сценариях работы с memcached.
Автор: dkrot