memcache vs memcached - сравниваем клиенты для PHP

Какой клиент лучше использовать при разработке на PHP - php-memcached или php-memcache? Все зависит от того, какие особенности Вам нужны (неужели?). Давайте сравним в двух аспектах - функциональность и производительность.
Функциональность
Клиентская библиотека php-memcache была разработана еще в 2004 году и сегодня существует уже довольно стабильная версия, которая используется в 99.9% проектах (использующих сервер Memcache). Большим недостатком этой библиотеки является ее ограниченные способности - она реализует только часть протокола мемкеша, и не позволяет использовать его дополнительные возможности (какие именно - чуть ниже).
Библиотека php-memcached была разработана сравнительно недавно, но уже успешно используется на некоторых крупных проектах (например, digg.com - из которого и вышла эта разработка). Самое главное ее преимущество - это полная реализация протокола, в том числе:
- CAS токены для версионирования ключей
- Обратные вызовы (callbacks)
- Метод getDelayed() позволяющий уменьшить время ожидания откладывая фактическое чтение ключей
- Поддержка бинарного протокола
- Возможность избежать сериализации используя igbinary
Производительность
Теперь давайте сравним производительность на практике.
Для этого напишем небольшой скрипт, который делает одинаковые операции с обоими клиентами:
<?
$ops = 10000;
$m = new Memcache();
$m->addServer('localhost');
$md = new Memcached();
$md->addServer('localhost', 11211);
echo "Test operations: {$ops}";
echo "<h3>get test</h3>";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $m->get( md5(rand(1000, 99999)) );
echo "Memcache: " . ($res['memcache']['set'] = microtime(true) - $s ) . "<br />";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $md->get( md5(rand(1000, 99999)) );
echo "Memcached: " . ($res['memcached']['set'] = microtime(true) - $s );
echo "<h3>set test</h3>";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $m->set(md5(rand(1000, 99999)), 2);
echo "Memcache: " . ($res['memcache']['get'] = microtime(true) - $s ) . "<br />";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $md->set(md5(rand(1000, 99999)), 2);
echo "Memcached: " . ($res['memcached']['get'] = microtime(true) - $s );
echo "<h3>delete test</h3>";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $m->delete(md5(rand(1000, 99999)));
echo "Memcache: " . ($res['memcache']['delete'] = microtime(true) - $s ) . "<br />";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ ) $md->delete(md5(rand(1000, 99999)));
echo "Memcached: " . ($res['memcached']['delete'] = microtime(true) - $s );
echo "<h3>combined test</h3>";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ )
{
$key = md5(rand(1000, 99999));
$m->set($key, 2);
$m->get($key);
$m->delete($key);
}
echo "Memcache: " . ($res['memcache']['combined'] = microtime(true) - $s ) . "<br />";
$s = microtime(true);
for ( $i = 0; $i < $ops; $i++ )
{
$key = md5(rand(1000, 99999));
$md->set($key, 2);
$md->get($key);
$md->delete($key);
}
echo "Memcached: " . ($res['memcached']['combined'] = microtime(true) - $s );
Как видно, скрипт просто выполняет основные операции сохранения, чтения и удаления ключей 10 тыс. раз и замерает их время для каждого клиента.
Результаты работы теста
Как видно из графика, производительность клиента php-memcache почти втрое выше, чем php-memcached. Следует учесть, что показатели при работе с бинарным протоколом почти не отличалась по времени от ASCII протокола.
Обновление
Благодаря внимательному читателю этого блога удалось выяснить, что сильные отличия в показателях тестов связаны с конкретными версиями и настройками библиотек. Проверка на других платформах дает отличие в производительности не более 10%.
В качестве вывода следует сказать, что клиент php-memcache лучше использовать, если функционала приложению полностью хватает (лучше, т.к. он быстрее). Если нужны расширенные возможности, то нужно использовать php-memcached. Но в этом случае нужно учесть понижение производительности.
Хотя важно заметить: даже не смотря на трехкратное различие в скорости работы, операции общения с сервером Memcache обычно занимают доли процента от времени исполнения приложения. Различие в скорости работ библиотек может быть заметно только при массовой работе с кешом, или же на огромных объемах данных.


Дякую за статтю. Тепер буде стимул і самому розібратися з memcache
Я бы добавил еще тест на мультигет
Здравствуйте!
Ваш тест на Ubuntu Netbook Remix:
Test operations: 10000
get test
Memcache: 3.74332213402
Memcached: 3.32582402229
set test
Memcache: 3.64596199989
Memcached: 2.93430399895
delete test
Memcache: 2.82469391823
Memcached: 3.00117897987
combined test
Memcache: 6.8338830471
Memcached: 7.90108919144
Разницы в 3 раза не наблюдается.
@vasa_c
Давайте сравним, укажите версии библиотек и самого сервера.
@Den Golotyuk
Ubuntu 9.10
PHP 5.3.2-dev
PHP Extension 20090626
Zend Extension 220090626
php_memcache version 2.2.5
php_memcached version 1.0.1
libmemcached version 0.31
Memcached 1.2.8
memcached 1.4.2
php-memcache 2.2.5
php-memacached 1.0.2
PHP 5.2.11
libmemcached 0.40
И того, различия в сервере и версии PHP, что скорее всего не может дать такой разницы. Я попробую провести тесты на других серверах и опубликую результаты.
Разве что php-memcached в 1.0.2 значительно ухудшился по сравнению с 1.0.1
Я тоже постараюсь сегодня проверить на FreeBSD
@vasa_c
Я нашел довольно интересную закономерность. Похоже что результаты тестов сильно искажены внутренней проблемой библиотеки memcached (судя по всему этой конкретной версии). Буду благодарен, если проведете тест на своей сторое: в одном случае соединение устанавливается с IP (127.0.0.1) в другом с доменным именем (localhost).
И как меняются ваши результаты в зависимости от этого?
Я не заметил у себя существенных изменений.
Кстати, rand() и md5() тяжеловаты, лучше их из тестируемого цикла выкинуть.
$keys = array();
for ($i = 0; $i < $ops; $i++) {
$key = md5(rand(1000, 9999));
$val = 2;
$keys[$key] = $val;
}
$s = microtime(true);
#for ( $i = 0; $i set(md5(rand(1000, 99999)), 2);
foreach ($keys as $key => $val) {
$m->set($key, $val);
}
echo “Memcache: ” . ($res['memcache']['get'] = microtime(true) - $s ) . “”;
$s = microtime(true);
#for ( $i = 0; $i set(md5(rand(1000, 99999)), 2);
foreach ($keys as $key => $val) {
$md->set($key, $val);
}
echo “Memcached: ” . ($res['memcached']['get'] = microtime(true) - $s );
В этом случае тесты ускоряются практически вдвое.
Но относительные скорости такие же.
FreeBSD 6.3-STABLE
PHP 5.3.2-dev
php_memcache 3.0.4
php_memcached 1.0.1
libmemcached 0.31
memcached 1.2.8
результаты для обоих библиотек снова различаются слабо
Извиняюсь за назойливость.
Написал свой тест - http://blgo.ru/t/blog/090527/mc/
Memcache Memcached
set-new 191 174
set-exists 185 173
get-exists 177 167
get-empty 170 159
delete-exists 182 164
delete-empty 174 160
Числа - микросекунды на один запрос.
@vasa_c
Спасибо! Важные результаты, т.к. php-memcached показывает лучшую производительность.
насчёт локалхоста.
Тут как-то решил протестить неочевидный момент и наткнулся на то, что кэш MySQL отрабатывал почти вдвое быстрее memcache. Сели с админом копаться, и в итоге пересадили memcache на сокет, чем довольно ощутимо разгрузили сервер. Не в разы, конечно, но разница во времени работы скриптов была хорошая.
Учтите, что разработка pecl-memcache брошена, и в связке с memcached-1.4.3+ операция delete всегда ведёт к ошибке.
88 CLIENT_ERROR bad command line format. Usage: delete [noreply]
Именно поэтому memcache::delete() всегда на порядок быстрее memcached::delete(), т.к. в случае с memcache он просто тупо не выполняется. Отсюда и огромная разница в тесте combined.
Касательно остальных сравнений, я также нашёл их неверными. Я проводил тесты на семи версиях memcached 1.2.6, 1.3.3, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, и каждую из них тестировал с pecl-memcache-3.0.4 в сравнении с четырьмя pecl-memcached 1.0.0, 1.0.1, 1.0.2 и 2.0.0. То есть было выполнено 7*4=28 тестов во всех возможных комбинациях перечисленных выше версий (начиная с memcached-1.4.3 все операции delete я отключил). Итог - везде оба экстеншна показывают приблизительно одинаковые скорости, а разницу в сотые доли секунд я списываю на сеть.
Спасибо за комментарий, действительно тесты составлены неправильно, как показал дальнейший анализ (это обсуждали выше).
P.S.
Вордпресс сожрал знаки больше и меньше, должно было быть так:
<88 delete c5c3478127f5e50e49b50a3846afe884 1
>88 CLIENT_ERROR bad command line format. Usage: delete <key> [noreply]
Админ, поправьте плиз в моём предыдущем комменте.
Заметил очепятку в названии страницы :
memcache vs memached
вместо
memcache vs memcached
Привет,
К сравнению клиентов хотел бы попросить прояснить такой момент (не скорость) - это обработка файловеров:
В “старом” php-memcache - есть встроенный механизм обработки отваливания половины кеша.
В php-memcached я такого не нашел.
Пример:
Имеем два memcached-сервера (srv1, srv2).
php-memcache - рапределяет ключи по серверам 50%/50% или в зависимости от веса сервера, но в любом случае получаем объем кеша равный сумме памяти выделенной на обоих серверах.
При падении srv2, то в первый раз переменные, которые были на srv2 не доступны, беруться из источника, но сам модуль уже видит, что srv2 недоступен и сохранение и последующий get происходит уже только с srv1.
Т.е. оставшийся сервер “подхватывает” кеш того, кто “упал”.
php-memcached - рапределяет ключи по серверам 50%/50% или в зависимости от веса сервера, но в любом случае получаем объем кеша равный сумме памяти выделенной на обоих серверах.
При падении srv2, модуль не меняет алгоритм распределения переменных по серверам, и тем самым получаем половина переменных в кеше (на srv1), а половина - без кеша (srv2 down), и будут без кеша, пока не поднимется srv2.
Вопрос: это такое различие в идеологии, или я где-то чего-то не досмотрел? и в php-memcached можно реализовать failover?
Задача - получить большой кеш, который можно расширять путем введения новых memcached-серверов.
Кто как делает?
Далее:
У memcached-сервера есть два режима работы: сингл и репликация.
Репликация очень приятная вещь в плане возможности получения и сохранения конкретного значения _на любой из серверов_. Либо сохранил на один, получаешь с другого. Также можно частично решить вопрос отказоустойчивости.
Т.е. по сути я могу сделать группы кеш-серверов:
с1 - репликация на с2
с3 - репликация на с4
с1 и с2 в группе (они хранят одни данные), размер кеша определяется одним сервером.
с3 и с4 в группе (они хранят одни данные), размер кеша определяется одним сервером.
Но с1 и с3 - можно использовать как рапределение кеша, в котором объем кеша равен сумме.
Вопрос: как эту структуру указать модулю php? либо первому либо второму?
Не нашел.
@kVn
Привет, спасибо за обширный вопрос.
1. Методы распределения ключей, включение и выключение фейл-овера и принцип поведения при падении узлов можно менять настройками:
http://ua.php.net/manual/en/memcache.ini.php
http://ua.php.net/manual/en/memcached.setoption.php
Поэтому не нужно смотреть на функционирование по умолчанию, выбирайте то поведение, которое Вам необходимо.
2. Лично я никогда не использовал репликацию в Memcache, поэтому опыта в этом нет. Могу только посоветовать задуматься об эффективности этого решения. Вы можете все 4 узла использовать под кеш без репликации. Тогда при падении одного из них Вы потеряете только 25% данных в кеше. Это вызовет скачок нагрузки, с которым обязана справиться Ваша система.
пересадите memcached на udp-порт или сокет
порадуют как результаты php-memcache относительно ее же на tcp
а также сведет все плюсы php-memcached на нет относительно php-memcache
Добрый день!
Есть ли в природе версия php_memcacheD.dll ? а то очень тяжело поддерживать 2 эти библиотеки, если разработку ведешь на Windows.
Вот тут гуру собрались!
Может кто-нибудь может подсказать как установить mamcacheD на php под windows? Облазил интернет - нет .dll
Отнекропощу на предыдущие камменты.
dll в природе не нашлась, а скомпилировать под win не вышло (хотя и не очень долго пытались). Поэтому для win написал свой эмулятор - http://blgo.ru/blog/2009/06/08/memcache/ , и по собственной практике работает он стаблильно. Может пригодится ещё кому-нибудь.
Test operations: 10000
get test
Memcache: 0.34285402297974
Memcached: 0.71569895744324
set test
Memcache: 0.39891600608826
Memcached: 0.73352599143982
delete test
Memcache: 0.19314312934875
Memcached: 0.7186918258667
combined test
Memcache: 0.55521202087402
Memcached: 2.1280429363251
@chernousov
Проблема с delete пофикшена в 3.0.5
А как установить php-memcached под gentoo? не получается