Gibson Cache Server

imageВ своей работе мне часто приходится использовать key-value хранилище для организации связи между процессами, хранения настроек системы, временных данных и т. д. Для данных целей я использую Redis. Меня он вполне устраивает, еще и hiredis всем библиотекам библиотека.
Буквально сегодня наткнулся на совсем новый проект — Gibson Cache Server. Первый коммит датирован 17 маем 2013 года. О чем и речь!

Gibson Cache Server — in-memory key-value база данных, в основе которой лежит структура данных — дерево, в то время как redis и memcached используют хеш-таблицу. Это позволяет использовать операции сразу над несколькими ключами.

Особенности, заявленные разработчиком:
  • Чрезвычайно быстрая
  • Используется настолько мало памяти, насколько это возможно
  • Поддерживает LZF сжатие
  • Поддерживает Time-To-Live (время жизни ключа)
  • Поддерживает функциональность для блокировки и разблокировки ключей
  • Ну и собственно самое главное — множественные операции

Установка


Сервер

Здесь ничего сложного нет. Исходники сервера можно взять здесь.
git clone https://github.com/evilsocket/gibson.git
cd gibson
сmake -G «Unix Makefiles» .
make
sudo make install

Конфигурационный файл находится в /etc/gibson/gibson.conf
К слову, сервер можно запускать с ключем -c, указывая путь к файлу конфигурации, и не устанавливать всё в системные директории.

Клиент

Исходники находятся тут.
Здесь всё аналогично. Единственное, что нужно отметить, в ./src/linenoise должны лежать исходники библиотеки linenoise.
После
git clone https://github.com/evilsocket/libgibsonclient
cd libgibsonclient
сmake -G «Unix Makefiles» .
make

У нас появляется «джентельменский набор»: заголовчный файл, сама библиотека и gibson-cli.

Демонстрация


Запуск клиента и список команд, которые доступны на данный момент.
Скрытый текст
deck@crunch ~/work/libgibsonclient $ ./gibson-cli -u /tmp/gibson.sock 

type :help or :h for a list of commands, :quit or :q to quit.

local> :h
	SET <ttl> <key> <value>
	TLL <key> <ttl>
	GET <key>
	DEL <key>
	INC <key>
	DEC <key>
	LOCK <key> <seconds>
	UNLOCK <key>
	MSET <prefix> <value>
	MTTL <prefix> <ttl>
	MGET <prefix>
	MDEL <prefix>
	MINC <prefix>
	MDEC <prefix>
	MLOCK <prefix> <seconds>
	MUNLOCK <prefix>
	COUNT <prefix>
	STATS
	PING
	SIZEOF <key>
	MSIZEOF <prefix>
	ENCOF <key>


Стандартные set/get
Скрытый текст
local> set 0 foo 5
<STRING> 5
local> get foo
<STRING> 5


И с использованием TTL.
Скрытый текст
local> set 3 bar hi!
<STRING> hi!
local> get bar
<STRING> hi!
local> get bar
<REPL_ERR_NOT_FOUND>


Ну и множественные get/set
Скрытый текст
local> set 0 score:user1  400
<STRING> 400
local> set 0 score:user2  100
<STRING> 100
local> set 0 score:user3  900
<STRING> 900
local> mget score
score:user1 => <STRING> 400
score:user2 => <STRING> 100
score:user3 => <STRING> 900
local> mset score 0
<NUMBER> 3
local> mget score
score:user1 => <STRING> 0
score:user2 => <STRING> 0
score:user3 => <STRING> 0



Производительность


На сайте разработчика присутствуют данные тестов, которые он провел над операциями get/set на машине
Intel Core(TM) i3-2130 CPU @ 3.40GHz with 8GB of RAM, с Debian Squeeze.
Сравнивал он свою базу данных с Redis. Результаты следующие:
Redis
Скрытый текст
$ redis-benchmark -c 1 -n 100000 -q -t SET
SET: 51519.84 requests per second

$ redis-benchmark -c 1 -n 100000 -q -t GET
GET: 49212.60 requests per second


Gibson
Скрытый текст
@ Created 100000 / 100000 in 1216ms
-- 82236.842105 Req/s

@ Verified : 100000 / 100000 in 1145ms

-- 87336.244541 Req/s


Если не лукавит, то производительность на этих операциях выше у Gibson на 60 процентов.

Заключение


В настоящее время уже есть библиотека для php. Также, обещают в скором времени добавить поддержку Nodejs.
Библиотека не может быть не сырой, но, несомненно, заслуживает внимания. Под мои задачи должна вписаться идеально. Посмотрим, что получится.
Поделиться публикацией

Похожие публикации

Комментарии 31

    +10
    Касательно части производительности. Вообще бенчмаркать редис в одну сессию относительно глупо. Redis имеет простой event-loop design и на деле при одном сокете будет каждый раз будет происходить context switch при вызове epoll_await. Как только мы увеличиваем кол-во сессий, даже просто в два раза (2 коннекта), у нас пропускная способность увеличится > 2 в два раза.

    Пример (i7 930 2.8GHz):
    root@tundra01h ~ # redis-benchmark -c 1 -n 100000 -q -t SET
    SET: 53590.57 requests per second
    
    root@tundra01h ~ # redis-benchmark -c 1 -n 100000 -q -t GET
    GET: 54794.52 requests per second
    
    root@tundra01h ~ # redis-benchmark -c 2 -n 100000 -q -t SET
    SET: 127064.80 requests per second
    
    root@tundra01h ~ # redis-benchmark -c 2 -n 100000 -q -t GET
    GET: 136612.02 requests per second
    
    root@tundra01h ~ # redis-benchmark -c 4 -n 100000 -q -t SET
    SET: 144300.14 requests per second
    
    root@tundra01h ~ # redis-benchmark -c 4 -n 100000 -q -t GET
    GET: 141843.97 requests per second
    
    


    Если кто-то может, побенчмаркайте gibson на нескольких сокетах.

    По теме, ну хорошо, внутри там дерево, а не хеш-таблица. Чем это лучше и для каких use-case?:) По идее должно быть лучше на бооооооольшом количестве ключей, когда сравнивать хеши уже дорого, а по дереву пробежаться быстрей. Есть желающие побенчмаркать на заполненой памяти?:)
      0
      Чем это лучше и для каких use-case
      Ну, это дает множественную замену…
      По идее должно быть лучше на бооооооольшом количестве ключей
      С чего бы? Для больших объемов длина хеша растет логарифмически, как и глубина дерева.
        0
        Я подумал что мета-информация ключей хранятся не в виде хеш-таблицы, а в виде дерева, а тут именно перезапись родительского ключа может заменить дочерние. Не так понял фичу:)
          0
          Из вашего сообщения я ничего не понял.
          Впрочем, разобрались и ладно
          0
          Хеш-таблицы на больших объемах страдают или от коллизий или от (не) эффективности использования памяти.
          0
          По теме, ну хорошо, внутри там дерево, а не хеш-таблица. Чем это лучше и для каких use-case?:) По идее должно быть лучше на бооооооольшом количестве ключей, когда сравнивать хеши уже дорого, а по дереву пробежаться быстрей.

          Ну откуда вы могли придумать такое? На получение одного ключа асимптотика дерева О(log N), а у хэш-таблицы О(1). Другое дело, что в случае с деревом появляется возможность работать с поддеревьями, в статье как раз пример про множественные get/set.

          Основная проблема хэш-таблиц — это соответствие количества бакетов количеству ключей. При хорошем алгоритме хеширования хорошее отношение размера памяти к количеству коллизий достигается при load factor < 0.7. Если динамически изменять размер, то производительность на запись резко падает и становится не сильно лучше, чем у деревьев. При этом абсолютные цифры не важны, главное — load factor, то есть отношение количества ключей к количеству бакетов.
            0
            Кстати, а если не изменять размер, а построить хэш-таблицу, чтобы в каждой корзине данные хранились в виде не простого связанного списка, а чего-то другого (другой хэш-таблицы с другим размером или еще лучше — с другим принципом хэширования; дерева) — можно ли тогда достичь хорошей скорости?
              0
              Нередко бывает так, что на практике разницу между деревом и хэш-таблицей, когда они в памяти, будет видно только после добавления десятков, а то и сотен миллионов ключей. Если вкладывать хэш-таблицу в хэш-таблицу, то от load factor уйти не получится. Если дерево — то придётся балансировать: много бакетов — мелкие деревья — быстрый доступ, мало бакетов — большие деревья — дольше доступ. Будет быстрее или медленнее — зависит от многих параметров. Асимптотически хэш-таблица в хэш-таблице будет работать за константу, дерево в таблице — за логарифм уже.

              Из преимуществ хэш-таблицы можно назвать независимость бакетов друг от друга, если конечно перебалансировку не делаете. То есть при добавлении любого ключа меняется только один бакет. Можно ставить по одному локу на бакет. При использовании деревьев почти всегда используют самобалансирующиеся деревья, при добавлении ключа может измениться до log N узлов дерева. То есть работа с деревом — только под локом на всё дерево.
                0
                есть неблокирующие алгоритмы для самобалансирующихся деревьев
                  0
                  Можно примеры, работающие на x86, в которых решена проблема ABA и не требуется специальный аллокатор?
                    0
                    не знаю насчет деталей, но вроде бы BTRFS использует подобный алгоритм
                      0
                      Вы со спинлоками точно не путаете?
            0
            Разработчик Gibson очень сетует на тему, что в redis-benchmark используется не multithreading, а multiplexing, что собственно не соответствует реальным условиям.
            Чтобы не переходить на другую машину, попробуйте что-нибудь типа
            for i in 1 2
            do
                redis-benchmark -c 1 -n 100000 -q -t SET &
            done
            

              0
              root@tundra01h ~ # for i in 1 2; do     redis-benchmark -c 1 -n 100000 -q -t SET & done
              [1] 13193
              [2] 13194
              SET: 57045.07 requests per second
              
              SET: 57012.54 requests per second
              
              
              [1]-  Done                    redis-benchmark -c 1 -n 100000 -q -t SET
              [2]+  Done                    redis-benchmark -c 1 -n 100000 -q -t SET
              


              Угумс, чуть больше чем двукратный прирост, но не 127krps.

              А знаете почему? Да потому что redis-benchmark написан тоже с использованием такого же event-loop и у него та же проблема. Запустив два одновременно мы избавились от тормозов в самом редисе, но context switch на epoll_wait в бенчмарках остался, и поэтому прирост маленький.

              В моем тесте ускорились 2 event-loop, в redis и redis-benchmark. При нескольких сессиях ускоряются оба. В вашем же случае ускорился только redis, redis-benchmark как работал неэффективно на одной сессии, так и продолжает:)
                0
                проще написать собственный бенчмарк, протокол у редиса — простейший
                или доработать существующий бенчмарк
                  0
                  Зачем дописывать, стандартный не плох, а есть и сторонние:)
            0
            Посмотрел документацию. посмотрел пример с хранением сессий пхп и кэша вордпресса. Все вроде очевидно и обычно. В чем соль? Или только производительность в синтетических тестах лучше?
              +3
              Например, там есть такое:
              mdel
              Description: Delete keys verifying the given expression.
              Parameters: key (string) The key prefix to use as expression.
              Return value: Mixed The integer number of deleted items in case of success, FALSE in case of failure.

              $gibson->mdel('f'); // Delete every f* key.
              

              Мемкешд и редис не умеют так (редис, разве что, через консоль). А это в свою очередь позволяет удалять все значения, например, для измененной категории:

              // Сохраняем страничку в кеше
              $gibson->set('/foo/', 'bar');
              // Меняем что-то там и кеширем
              $gibson->set('/foo/', 'egg');
              // Выносим каскадно все дочерние страницы из кеша
              $gibson->mdel('/foo/');
              

              «Delete every f* key.» — очень доходчиво, как по мне :)
              0
              Я только не понял, зачем сравнивать персистентный redis с заметно большим фунционалом, а не с банальным memcache'ом. Кроме того, нет сравнения производительности операций по префиксам, что нивелирует эффект от бенчмарков, оставляя пустое «Под мои задачи должна вписаться идеально».

              Плюс есть специфически работающий лок, под который я сходу не могу придумать кейсов.
                0
                там из редис вытянуты необходимые исходники
                +1
                  0
                  Больше он конечно похож на memcached чем на redis, как я понял он не сохраняет базу на диск и восстанавливаться не умеет. Единственным плюсом перед memcached вижу блокировки, чего нехватает memcached. И тут возникает вопрос что делает другой клиент если кто-то поставил блокировку: он ждёт, он получает старое значение или он получает ошибку?

                  В драйвере для php нет pconnect(persistent connection)… А это значит каждая вновь запущенная php'шка будет задерживаться на открытии соединения от нескольким миллисекунд до 3 с лишним секунд. 3 секунды нужны для ожидания Syn пакета, который отправляется не ранее 3 секунд. Это происходит в моменты когда нагрузка на сервер внезапно возрастает по каким либо причинам и вместо того чтобы работать php'шки будут подвисать на коннекте к gibson на 3 сек, что недопустимо. Можно конечно через pfsockopen попробовать, но вот умеет ли gibson это?

                  Еще не плохо бы в мультигет запросах заиметь некий метаязык который бы позволил в одном запросе в случае хита получить ещё что-то с ключом зависимым от значения полученного ранее. Например записываешь список пользователей и при записи указываешь список зависимых ключей, а когда получаешь список из N пользователей и если такой список есть, сразу выдать ещё N значений с атрибутами пользователей. Экономия сотен микросекунд.
                    +1
                    Автор, спасибо тебе за находку. Учитывая, что оно может обновлять TTL, удалять и менять значения по паттерну, можно проще и удобнее работать с кешем без необходимости заранее знать все ключи (для удаления, обновления). Ждем питоновскую библиотеку.
                      0
                      Объясните мне пожалуйста, тупому, чем он лучше Тарантула? В Тарантуле имеем всё то же самое, + совместимость с мемкешом по протоколу. Может тем, что у него меньше фич? Это иногда плюс. Но с другой стороны, драйвера для разных языков, поддержка дистрибутивов и платформ — это всё для Гибсона в зачаточном состоянии. Тарантул часто не используют именно потому, что недостаточная поддержка того или иного дистрибутива. Или community соберётся и всё сама напишет?

                        0
                        Да наверное ничем не лучше. А вам бы какой-нибудь tutorial или quick-start написать… Я лично раза 2 пытался к Tarantool подступиться, но бысто отказывался, т.к. не находил с чего начать.
                        Имею в виду, конечно же, не то, как его установить, а то, как работать с данными в нём. Имею в виду что-то вроде tarantool.org/tarantool_user_guide.html#data-and-persistence но побольше примеров. Желательно приближенных к реальности.
                          0
                          использую тарантул более года, реализовано два проекта (оба социальные игры),
                          принципиально доволен,
                          администрирование нормальное, саппорт вменяемый.

                          что касается инструкции — то мне кажется, что у команды разработчиков столько много идей, что они их реализовывать не успевают, так что руководство писать некому в маленькой команде :)

                          Ждем новых фич от тарантула!
                            0
                            Рад за вас.

                            Но необходимость Tutorial / Quick Start это не отменяет.
                            0
                            Мы покурили тут ваш комментарий, поняли что т.к. Тарантул по факту база данных + апп сервер, то проблема в отсутствии вменяемого пакет менеджера для наших модулей на Луа.

                            Для того, чтобы написать краткий туториал который будет работать не у гика, нужно чтобы всё делалось одной-двумя стандартными командами, типа apt-get install lua-module; sudo service tarantool restart

                            Сейчас тарантул может очень много, но для того чтобы от этого иметь профит надо шарить в Луа…

                            Будем пилить дальше, спасибо за коммент.
                          • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            1) версия еще в статусе unstable
                            2) сам-то используешь или только собираешься?
                            3) аналоги TokyoTurant fallabs.com/tokyotyrant/, tarantool tarantool.org
                              0
                              Cам являюсь пользователем Tokyo, использую его АПИ для создания собственного NoSQL хранилища.
                              Принципиально оно меня удовлетворяет, как хранилище с разными типами ключей TREE и HASH.
                              Другие АПИ (https://github.com/fmela/libdict) иногда валят проект сигфолтом.
                              Используется для хранения лайков и других счетчиков. Нагрузка на проект 5-7К запр в сек. в пике бывает 50К.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое