memcounter — это высокопроизводительный демон для хранения счётчиков.
Почему memcounter, а не база данных? Потому memcounter быстрее, он хранит всё в памяти и оптимизирован под работу со счётчиками.
А чем memcached не устроил? memcached предназначен для кеширования данных, а не множества маленьких счётчиков к которым очень часто обращаются. Плюс memcounter позволяет за один запрос выполнить пакет команд. Например, в memcounter хранится статистика по баннеру с четырмя полями — показов всего (1), показов за сегодня (2), ограничение показов всего (3), ограничение показов в день (4). Мы показали баннер (с идентификатором 123) и хотим обновить счётчики. Всё это делается одним запросом из четырёх команд:
inc 1-123;inc 2-123;dec 3-123;dec 4-123;\nа в ответ получаем значения счётчиков после внесения изменений:
1001;101;999;99;\nхотите знать что было до внесения изменений, пожалуйста:
get 1-123;get 2-123;get 3-123;get 4-123;inc 1-123;inc 2-123;dec 3-123;dec 4-123;\nа в ответ:
1000;100;1000;100;1001;101;999;99;\nПрикольно, да? Сам тащусь ;)
Установить libevent http://www.monkey.org/~provos/libevent/ и Judy http://judy.sourceforge.net/ если они ещё не стоят.
Скачать memcounter-0.0.5.tar.gz.
gunzip memcounter-0.0.5.tar.gz tar -xf memcounter-0.0.5.tar cd memcounter-0.0.5 ./configure make sudo make installЕсли не получилось, пишем мне гневное письмо на saterenko ну тут понятное дело собака gmail.com. Не забываем в письмо вставить то что вывело на экран и ждём пока я отвечу. Либо разбираемся сами и всёравно пишем мне гневное письмо о том что случилось и как это полечилось.
Сначала про опции запуска. Пишем memcounter -h и видим:
Usage: memcounter [options]
Options:
-c <size> Specify connection pool size (1024 by default)
-d Daemonize (not daemonized by default)
-h Display this information
-l <ip> Specify listen IP ("127.0.0.1" by default)
-m <size> Specify initial memory pool size in Mb (1Mb by default)
-o <file> Specify the file to read data ("./memcounter.dat" by default)
-p <port> Specify listen port (11711 by default)
-P <file> Specify the pid file ("./memcounter.pid" by default)
-t Just test options and exit
-u <user> Set process user (user not changed by default)
-v The program version
-V Verbose (off by default)
где:
Если при запуске выдаёт что-то типа
memcounter: error while loading shared libraries: libevent.so.1: cannot open shared object file: No such file or directoryили
memcounter: error while loading shared libraries: libJudy.so.1: cannot open shared object file: No such file or directoryпишем в консоли export LD_LIBRARY_PATH=/usr/local/lib
Формат запроса следующий:
[команда 1] [идентификатор 1] [значение 1];[команда 2] [идентификатор 2] [значение 2];...\nПараметр [значение] можно опускать, тогда для set оно принимает значение 0, для inc и dec — 1. Синтаксис является строгим. Перед командами не может быть никаких символов (между ; и командой тоже ничего не должно быть). Между командой и идентификатором должен быть пробел. В конце запроса должен быть символ \n. Допускается наличие символа \r. Не допускается использование идентификаторов частью которых являются другие идентификаторы. Например, одновременно не допускается использовать идентификаторы 1, 1-1, 1-1-1 и тп.
По команде flush данные сбрасываются в файл заданный опцией -o. Комманду flush можно использовать как саму по себе, так и в рамках пакетного запроса. Команда возвращает количество записанных пар идентификатор-значение. Команда flush выполняется последовательно, как и остальные команды.
Примеры:
set 1-2-3 10;set 3-2-1\n ответ: 10;0\n dec 1-2-3;inc 3-2-1\n ответ: 9;1\nКак видно из примера, демон в ответ для каждой команды возвращает значение счётчика.
Ограничения:
Варианты ответов в случае ошибки:
inc 1-1-1;int 1-1;get 1-1\n ответ: EBC 10\nВсе E* ошибки прерывают выполнение запроса. I и N не прерывают выполнение запроса, а просто заменяют собой значение счётчика. Например:
inc 1-1-1;inc 1-1;get 1-1\n ответ: 1;I;N\n
Допустим, нам необходимо хранить счётчики заходов на сайт из определённой страны в определённое время суток. Таким образом у нас есть идентификатор сайта (site_id), идентификатор страны (country_id) и час (hour).
Запускаем демон:
memcounter -dСайт 10, страна 32, час 10:
inc 1-10;inc 2-10-32;inc 3-10-32-10\n ответ: 1;1;1;\nСайт 10, страна 15, час 10:
inc 1-10;inc 2-10-15;inc 3-10-15-10\n ответ: 2;1;1;\nСайт 10, страна 15, час 10:
get 1-10;get 2-10-15;get 2-10-32;get 3-10-15-10;get 3-10-32-10\n ответ: 2;1;1;1;1;\n
Нормального тестирования не проводил, ограничился сурогатом на php:
<?php
$time = time();
$fp = fsockopen('localhost', 11711);
if (!$fp) {
die("cant open socket");
}
for ($i = 0; $i < 1000000; $i++) {
fwrite($fp, "inc 1-".rand(1, 500000)." $i;\n");
while (true) {
$c = fgetc($fp);
if ($c == "\n") {
break;
}
}
}
fclose($fp);
echo 1000000 / (time() - $time), " sec\n";
?>
получилось:
vinny$ php send1.php 15384.615384615 sec
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE 17414 php 64.7% 0:35.51 1 15 30 1.14M 4.18M 3.02M 47.4M 17385 memcounter 41.6% 1:17.50 1 9 27 1.11M 1.21M 1.58M 28.8Mэто на моём MacBook Pro:
Model Name: MacBook Pro 15" Model Identifier: MacBookPro1,1 Processor Name: Intel Core Duo Processor Speed: 2 GHz Number Of Processors: 1 Total Number Of Cores: 2 L2 Cache (per processor): 2 MB Memory: 2 GB Bus Speed: 667 MHz Boot ROM Version: MBP11.0055.B08 SMC Version: 1.2f10 Serial Number: W86430E1VWW Sudden Motion Sensor: State: Enabledа это на нашем с trent-ом сервере (Dual Core AMD Opteron(tm) Processor 270 / 2Ghz / 2Gb RAM / Linux ghost 2.6.15-26-amd64-server #1 SMP Fri Sep 8 20:33:15 UTC 2006 x86_64 GNU/Linux), сервер под лёгкой нагрузкой по %CPU соотношение примерно как на ноуте:
vinny@ghost:~$ php send1.php 23809.5238095 secЕщё раз, это не тест, это сурогат чтобы понять порядок производительности. Только щас заметил, — не "sec", а "req/sec".
По количеству "поглощаемой" памяти, на моём ноуте (32 бита) 10.000.000 записей (двухуровневый идентификатор типа 33-12345) заняло порядка 84Мб памяти. Сброс (flush) этого безобразия в файл занимает порядка 3-4 секунд, поднятие из файла тоже порядка 3-4 секунд. Файл занимает порядка 130Мб.
Вопросы и пожелания слать на saterenko ну тут понятное дело собака gmail.com.