Comments
Какой из клиентов использовали? И почему?
Были какие то неявности, странности и баги, кроме приведения кодировок?
Какой из клиентов использовали? И почему?

выбран php-handlersocket по той причине, что это враппер над libhandlersocket.
Если сравнивать с производительностью HS клиента, то за счет медлительности самого РНР, все нативные клиенты, написанные на нем, проигрывают в производительности врапперам. Далеко ходить не надо: сравнимаем производительность мемкешед и редис клиентов. Какой используешь ты и почему?

Про PEAR клиента — та же история, только с боку.
Какой используешь ты и почему?

Пока никакой, не было подходящей задачи. На будующее интересуюсь. А то обычно для новых технологий: куча разных библиотек, 1 вообще не работает, во 2-ой встроенное кэширование, которое нельзя сбросить, в 3-ей 100500 багов и тишина, остальные худо-бедно работают, если знать правильные заклинания и иметь набитые шишки.
Я думал ты про handlersocket. Для memcached использую memcache, ибо меньше глючит.
однако не нативнеый IMemcached,
а реализованный в ввиде экстеншена.

согл, memcached — это враппер над libmemcached, а она часто глючит. А вот авторы memcache свое творение довели до ума, реализовав практически весь протокол. Я, кстати тоже его использую.
+1 про шишки. Иногда офигеваешь, и только после многочасовых танцев с бубном понимаешь причину бага. Взять хотя бы авто-сериализацию в php-memcached после 2000 символов.
при выдачи nginx напрямую из мемкеша (ngx_memcached) есть небольшой патчик
который анализирует код возврата memcached, и если там хранилась «сжатая» информация, то выставляет соответствующие заголовки.
рекомендую+ съэкономите на памяти.
а вообще статья зачетная, я про это тоже не знал.
Вообще-то в самом пхп-мемкешед модуле много магии.
Были какие то неявности, странности и баги, кроме приведения кодировок?
Долго провозился с кодировками. Первое время думал что бага, отписал автору.

Решил эту проблему, только после применения нативного протокол через телнет. хотел выяснить, кто виноват: враппер, либа или ограничена возможность HS. Сперва думал, что это бага.
Ну, если быть откровенным, я часть функционала, связанного с memcached отлаживаю, используя телнет. Как-то Хабросообществом была недооценена моя статья, в которой я по шагам расписал, как я отлавливал багу в libmemcached. А вот по рассылкам php-hiload да и в самом Сообществе проскакивали похожие баги падения memcached. Так что хочу обратить внимание, что знание нативного протокола всегда помогает в отладке.

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

Спасибо за ответы.
А у меня очень большой вопрос: HandlerSocket позволяет осуществить сортировку результата? Или тут тупо PK/K lookup, а сортировать на клиенте, теряя выигранное (и даже проигрывая еще больше)?
Даже хрен с ней, сортировкой, HandlerSocket отдаст записи в том же порядке, в каком были указаны ID в запросе? Типа queryMulti (ID=3,5,1,9) — записи вернутся отсортированые по ключу, по запросу или неопределено?
Отвечу сразу на два вопроса.
1. Сортировка записей в HandlerSocket не предусмотрена.
2. При Multi-Get записи будут возвращаться в том порядке, в котором были указаны ID в запросе. Вот пример:

  1. <?php
  2. // connect
  3. $hs = new HandlerSocket('127.0.0.1', 9998);
  4. if($hs) {
  5. print 'Connected to HandlerSocket<br/>';
  6. } else {
  7. print 'Can not connect<br/>';
  8. die();
  9. }
  10. //index
  11. $index = $hs->openIndex(
  12. 1,
  13. 'mysql',
  14. 'user',
  15. HandlerSocket::PRIMARY,
  16. 'Host,User,Select_priv'
  17. );
  18. if($index) {
  19. print 'Index opened<br/>';
  20. } else {
  21. print 'Can not open index<br/>';
  22. echo $hs->getError(), PHP_EOL;
  23. die();
  24. }
  25. $id = rand(1,1000);
  26. // find
  27. //$result = $hs->executeSingle(1, '=', array($id), 1, 0);
  28. // Чтение данных Multi-Get (find по тому же индексу)
  29. $retval = $hs->executeMulti(
  30. array(
  31. array(1, '=', array('localhost'), 1, 0),
  32. array(1, '=', array('%'), 1, 0),
  33. array(1, '=', array('127.0.0.1'), 1, 0)
  34. )
  35. );
  36. var_dump($retval);

______________________
Текст подготовлен в Редакторе Блогов от © SoftCoder.ru

А вот результат:
  1. Connected TO HandlerSocket<br/>Index opened<br/>array(3) {
  2. [0]=>
  3. array(1) {
  4. [0]=>
  5. array(3) {
  6. [0]=>
  7. string(9) "localhost"
  8. [1]=>
  9. string(16) "debian-sys-maint"
  10. [2]=>
  11. string(1) "Y"
  12. }
  13. }
  14. [1]=>
  15. array(1) {
  16. [0]=>
  17. array(3) {
  18. [0]=>
  19. string(1) "%"
  20. [1]=>
  21. string(6) "daobay"
  22. [2]=>
  23. string(1) "Y"
  24. }
  25. }
  26. [2]=>
  27. array(1) {
  28. [0]=>
  29. array(3) {
  30. [0]=>
  31. string(9) "127.0.0.1"
  32. [1]=>
  33. string(4) "root"
  34. [2]=>
  35. string(1) "Y"
  36. }
  37. }
  38. }

______________________
Текст подготовлен в Редакторе Блогов от © SoftCoder.ru
Спасибо, это отличная новость. Тогда можно отсортировать и выбрать ID в нужном порядке посредством SQL, а данные уже через HandlerSocket.
UFO landed and left these words here
Очень даже не экзотический, а вполне работающий.
Это обобщенный подход, дающий лучшую среднюю производительность, чем сложная сортировка и выемка данных в одном запросе. Т.е. вместо одного запроса всегда два. В моем случае второй запрос можно заменить на HandlerSocket.

$IDs = SELECT id FROM megatable WHERE a=10 AND b>9000 AND c BETWEEN 1111 AND 100500 ORDER BY a DESC, c ASC, g ASC

$Data = SELECT * FROM megatable WHERE ID IN ($IDs) ORDER BY FIND_IN_SET(ID, $IDs) ASC
То же самое касается JOIN'ов. Собираем нужные нам id'шники по всяким разным условиям, сортируем как хотим, а непосредственно загрузку сущностей делаем через HS. По сравнению с JOIN думаю это даст хороший выигрыш на таблицах больших объемов.
UFO landed and left these words here
Ok, посмотрите план SQL-запроса с JOIN и без.
Пусть t1 — полное время выполнения простого запроса (включая сетевые задержки),
t2 — то же самое для сложного запроса (с JOIN'ами),
t3 — общее время время выполнения запросов HS по получению сущностей.
Наша идея в том, что t2 > t1 + t3.
UFO landed and left these words here
согл, нужно уметь деноморолизовать БД.

В соцсетях, где используется шардинг — вообще отказались от джоинов.
Так а я о чем говорю. Делаем денормализацию, получаем меньше запросов с JOIN'ами и больше запросов по PK/K, которые отправляем через HandlerSocket.
UFO landed and left these words here
А не могли бы сделать тест производительности, чтобы можно было сравнить быстродействие расширения, PEAR пакета и HSPHP? Спасибо!!!
РЕАR точно делать не буду, так как ./configure --without-pear
а HS & php-handlersocket сделаю. Выиигрыш в копейки, но все же, у кого большие нагрузки — приятно.
0.0003 микросекунды на запрос? Это что-то типа 3 млрд. запросов в секунду? :) Можете попробовать с помощью DBench протестировать, оно там умеет на автомате базу случайными данными забивать (см. example) и графики строить. Для вашего случая там надо будет только DBench_Test_Abstract отнаследовать с переопрелением protected function test() на обращение к базе через Handler Socket.
Что-то типа 3 млрд. запросов в секунду — это что-то типа 1 такта процессора на запрос? :) Ясно же, что не может быть такой производительности.
Это был риторический вопрос, сарказм т.е. :)

Автор пишет:
время грязное в микросекундах, с учетом на инклуды и инстанс класса
0.005791

Я потому и переспрашиваю, может в данном случае имеются ввиду «секунды» и нужно подправить топик.
там все относительно
показать что быстрее и в каких пропорциях
С каких пор время в 0.005791μс — это «относительно»?
И я правильно понимаю, что тест содержал одну итерацию?
Время без инклудов и инстансов, чисто одна выборка по PK
$t1 = microtime();
$retval = $hs->executeSingle(2, '=', array('60187'));
echo microtime()-$t1, PHP_EOL;
$t1 = microtime();
$res = $rs->select('=','60187');
echo microtime()-$t1, PHP_EOL;

У вас там явная ошибка: вместо microtime() нужно писать microtime(true).
Немного смущает отсутствие документации на php-hanlpersocket

Возможно, немного поможет $ php --re handlersocket
и чем же?
не более чем Class synopsis¶

HandlerSocket {
/* Constants */
const HandlerSocket::PRIMARY;

/* Methods */
__construct ( string $host, int $port, [ array $options ])
public bool openIndex ( int $id, string $db, string $table, string $index, string $fields )
public mixed executeSingle ( int $id, string $op, array $fields [, int $limit, int $skip, strint $modop, array $values ] )
public mixed executeMulti ( array $requests )
public int executeUpdate ( int $id, string $op, array $fields, array $values [, int $limit, int $skip ] )
public int executeDelete ( int $id, string $op, array $fields [, int $limit, int $skip ] )
public bool executeInsert ( int $id, array $values )
public string getError ( void )
}

Ну, это, конечно, не документация, поэтому и помочь может лишь немного. Но имхо, все же это лучше, чем рыться в исходниках, чтобы просто узнать, какие параметры принимает метод.
вообщее-то я в таких случаях лезу в исходники. Но тут исходники показали, что все параметры тупо передаются на сервер через libh..s…
т.е. копаем логику в граблях самого HS
мои планы:
написать утилитку, аналог redis-cli,
чтоб лучше отлаживать, проверять ключи. Все-таки telnet -это вчерашний день.

есть желание (нет времени) доработать php-handlersocket в сторону:
— фильтрация данных, чтоб это делать не средствами РНР
— сюда же возможность использования простых шаблонов аля '%'
— JOIN рекордсетов. Пока думаю как лучше сделать.

Сегодня перевел статистику (очень тяжелый update на 17 мин) на HS. Выполняется чуть более нескольких минут. Все в режиме тестирования.
Готов принять участие в тестировании, если что.
Нативная сортировка + фильтрация в расширении, это клёво. Всё лучше выносить за уровень php. А как планируется реализовать JOINы? Через серию запросов в ресширении, и возвращении полного набора данных (уже сджоиненного) в php?
с джоинами все просто:
1) делаем выборку по ключу T1.key1,
2) делаем выборку по ключу T2.key2 = T1.key1,
выборки у нас отсортивованы по клюючам.
3) начинаем итерировать полученный рекордсет T1 и класть его в массив,
одновременно сливая его с рекордсетом T2.

Возможные грабли или подводные камни:
мы не можем вытащить весь рекодсет простым запросом (SELECT *… WHERE region_id=1). Мы обязаны указать значение LIMIT. Соответственно нужно вытягивать либо с заданным LIMIT очень большое число, либо порциями.
еще нужно заранее определить размеры буферов под рекордсеты. Можно взять по максимуму, но это не будет эффективно. можно запрашивать их динамически, зная, например, что под одну запись буден не больше 1К. В общем здесь проблемки есть
хотелось бы что-то вроде executeMulti('ID', 'sql', 'SELECT id FROM foo WHERE bar=0')
чтобы избежать промежуточного хранения значений ключей в памяти средствами пхп, очень уж избыточно по 60 лишних байт на каждый элемент массива.
что пока не выяснено, но хотелось бы:

public mixed executeSingle ( int $id, string $op, array $fields [, int $limit, int $skip, strint $modop, array $values ] )
последние два параметра: strint $modop, array $values, судя по описанию представляют собой аналог getset (или взять и уничтожить). Однако, у меня пока это не получилось.
Вот вечно так… наоткрываешь страниц, а потом такие косяки вылазят.
Да, вспомнил про одну багу:

когда отлаживался, то сбросил телнет по kill -9
последующие запуски скрипта приводили к его зависанию.

приходилось перезапускать мускуль.
это уже лишнее, есть тесты на сравнение с мемкешом и прочими key/value хранилищами (выложу в следующем топике).
тесты есть в презенташке моего доклада на AddConf
www.slideshare.net/akalend/handdler-socket-in-the-addconf

сейчас нет времени писать топики, работаю над другими интересными проектами,
есть положительные результаты, но выкладывать как статьи еще рано.
Only those users with full accounts are able to leave comments. Log in, please.