Показаны сообщения с ярлыком sharding. Показать все сообщения
Показаны сообщения с ярлыком sharding. Показать все сообщения

среда, 14 июня 2017 г.

RedisSharding - SCAN command added

В Standard ML версию RedisSharding (собирается при помощи MLton and PolyML) (https://github.com/kni/redis-sharding-sml) добавил поддержку команды SCAN.

При этом есть нюанс: курсор - это целое произвольной точности.
То есть, если клиенты оперируют с курсором как со строкой или числом произвольной точности, то они будут работать с командой SCAN, иначе нет.

вторник, 13 сентября 2016 г.

Lazy ByteString или Haskell - кому сказать прощай?

Наверное вы уже знаете, что у меня есть 3 версии RedisSharding: Perl версия, Haskell версия с использованием Lazy ByteString и Strict Haskell версия с использованием attoparsec.

В старой заметке Redis Sharding: Lazy vs Strict (http://by-need.blogspot.com/2013/01/rs.html) утверждается, что Lazy и Strict версии примерно одинаковы по производительности.

Но, так как RedisSharding используется в основном для больших MGET команд, сравнение Lazy и Strict версии в pipeline режиме не производилось. А вот сейчас как-то случайно...

Оказалось, что Lazy версия в pipeline режиме имеет очень маленькую производительность.

cpuset -l 0 ./redis_sharding --port=8090 --nodes=127.0.0.1:8081,127.0.0.1:8082,127.0.0.1:8083,127.0.0.1:8084 +RTS -s -N1 -A10M -qa


Lasy версия.

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 1
SET: 20000.00 requests per second
GET: 19801.98 requests per second
MSET (10 keys): 9891.20 requests per second

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 2
SET: 195.46 requests per second
GET: 196.31 requests per second
MSET (10 keys): 196.29 requests per second

Strict версия.

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 1
SET: 16103.06 requests per second
GET: 15898.25 requests per second
MSET (10 keys): 9337.07 requests per second

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 2
SET: 25906.74 requests per second
GET: 25839.79 requests per second
MSET (10 keys): 12285.01 requests per second


Первой мыслью было, что где-то что-то не так сделал в Lasy версии. Но результаты профилирования опровергают это, тем, что нет какой-то явной медленной функции:

Lasy версия
COST CENTRE                           MODULE                               %time %alloc
throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal               29.8    0.7
MAIN                                  MAIN                                  11.9    0.8
server_responses                      RedisSharding                         11.2    0.9
client_reader                         RedisSharding                         10.1    5.6
send                                  Network.Socket.ByteString.Lazy.Posix   9.5    8.1
...
key2server                            RedisSharding                          2.3    0.4

Strict версия
COST CENTRE                           MODULE                              %time %alloc
endOfLine                             Data.Attoparsec.ByteString.Internal  13.0    7.5
decimal                               Data.Attoparsec.ByteString.Char8     11.0    8.2
servers_sender                        RedisSharding                        10.6   13.1
throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal              10.4    0.5
client_reader                         RedisSharding                         9.8    5.5
...
key2server                            RedisSharding                         1.7    1.2

Но где тут 20 кратное замедление? Возьмем для ориентира чистую функцию key2server, которотая под ключу определяет на какою ноду отправлять данные. В обоих версиях время выполнения ее в процентной отношении соизмеримо. Значит профайлер не показывает обнаруженное 20 кратное замедление. О чем это может говорить? Неужели, причина кроется где то глубоко в Haskell RTS? Что, выходит, Харпер прав?

P.S. Для не threaded режима работы RTS Haskell, цифры аналогичны.

P.S.S. Нужно для объективности еще провести исследования.

среда, 11 июня 2014 г.

Mio benchmark (GHC 7.8)

Наконец-то добрался протестировать производительность Mio ("Scalable IO Manager"), который появился в GHC 7.8. Предыдущей менеджер при переключении в threaded режим существенно терял производительность.

Тестирование производилось, используя реальное приложения, написанное на Haskell: RedisShardnig (http://github.com/kni/redis-sharding-hs-strict). Оно собиралась при помощи команды:

ghc -threaded -feager-blackholing -rtsopts -O2 --make redis_sharding.hs

Результаты сравнения Mio и старого менеджера.

FreeBSD (мой ноутбук: Intel(R) Pentium(R) CPU P6200)
Requests per second
          |       | threaded
----------------------------
GHC 7.8.1 | 11560 | 12853
GHC 7.6.1 | 11210 |  7686

Linux (какой-то сервер c 8 ядерами)
Requests per second
          |       | threaded
----------------------------
GHC 7.8.1 | 16393 | 17605
GHC 7.6.1 | 16583 |  9354

А вот данные как изменяется производительность с ростом количество рабочих OS threaded (параметр запуска -N):

GHC 7.8.1 threaded, Linux, 8 ядер.
... +RTS -N1 ( 4 OS threads) - 15873.02 requests per second
... +RTS -N2 ( 6 OS threads) - 28735.63 requests per second
... +RTS -N3 ( 8 OS threads) - 29411.77 requests per second
... +RTS -N4 (10 OS threads) - 26954.18 requests per second

Как видим, с выходом нового менеджера ввода-вывода производительность не только перестала падать при переключении в threaded режим, а даже стала возрастать!

среда, 6 февраля 2013 г.

RedisShardnig и GHC threaded

В последней версии RedisShardnig (http://github.com/kni/redis-sharding-hs-strict) буферизация данных и отправка их в сокеты происходит в отдельных user-lavel threads (forkIO), которым данные передаются посредством MVar переменных.

Разумеется, использование MVar имеет накладные расходы, но какие? Может, не смотря на некоторое усложнение кода, выгодней буфера таскать с собой?
Набросал простенький тестик, и кроме MVar добавил также RefIO. Тест показал, что и MVar и RefIO очень быстры и их можно использовать без опаски замедлить скорость выполнения программ.

Но тут я заметил, что компилировал без включения threaded ражима. А этот режим нужен, чтобы задействовать kqueue и epoll.
Скомпилировал я тесты в threaded режиме, запустил и увидел, что следует минимизировать их использование.

Но каждый тест является синтетическими, поэтому я решил вязать реальное приложение (RedisShardnig) и сделать версию с минимальным использованием MVar. В этой версии для буферизации не будут использоваться отдельные user-lavel threaded, с которыми общение происходит посредством MVar, а все буферизация будет происходить в рабочих threads и буыера будет таскаться с собой. Конечно множество MVar все равно останется, ведь при каждой операции записи чтения данных из сокетов используются MVar.

Текущая версия RedisShardnig (в таблице обозначена как mvar) и новая версия (в таблице обозначена как self) были собраны без и с поддержкой threaded режима и протестированы на производительность при помощи redis-benchmark (в 10 потоков). За RedisShardnig находилось 4 Redis node. Процессор имел 8 ядер. В таблице приведены тысячи SET запросов в секунду.

                 -P 1  -P 100
mvar             19      83
mvar threaded    10      93
self             23     116
self threaded    13     107

mvar -N2         21     166
self -N2         26     208

mvar -N4         23     232
self -N4         25     263


-P 100 - обозначает режим Pipeline с 100 команд в одном пакете (смотри redis-benchmark). То есть в этом режиме в 100 раз снижено количество отправки и получения данных из сокета.

Из таблицы видно, что для приложений с интенсивной сетевой нагрузкой включение threaded режима снижает производительность в 2 раза.
Впрочем и для приложений с другим характером нагрузки также заметно снижение производительность при использование threaded режима.
А не использовать threaded режим нельзя, так как для сетевых приложений необходим kqueue и epoll.
Кстати, разработчики GHC игнорируют это упущение.

пятница, 25 января 2013 г.

RedisSharding Benchmark

Let's go on testing RedisSharding, but this time we'll take redis-benchmark as the client. The conditions are the same.
Port:
6380 Redis (1 redis-server)
8000 RedisSharding (for 4 nodes)
9000 twemproxy (nutcracker) (for 4 nodes)

./redis_sharding --port=8000 --nodes=127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384 +RTS -s -N4 -A10M -qa

> redis-benchmark -p 8000 -n 10000 -c 10 -q -t set,get
SET: 24038.46 requests per second
GET: 24038.46 requests per second

> redis-benchmark -p 9000 -n 10000 -c 10 -q -t set,get
SET: 69444.45 requests per second
GET: 58823.53 requests per second
And now we'll enable the Pipeline mode and will be increasing the number of commands in one Pipeline gradually.
> redis-benchmark -p 8000 -n 10000 -c 10 -q -t set,get -P 100
SET: 217391.30 requests per second
GET: 250000.00 requests per second

> redis-benchmark -p 9000 -n 10000 -c 10 -q -t set,get -P 100
SET: 277777.78 requests per second
GET: 312500.00 requests per second
However, RedisSharding uses all the 4 core sets while the twemproxy is one-thread.
Let's launch the client with one connection:
> redis-benchmark -p 8000 -n 10000 -c 1 -q -t set,get -P 100
SET: 70422.54 requests per second
GET: 76335.88 requests per second

> redis-benchmark -p 9000 -n 10000 -c 1 -q -t set,get -P 100
SET: 227272.73 requests per second
GET: 263157.91 requests per second
But why should we abandon usage of all the processor's core sets?
> redis-benchmark -p 6380 -n 10000 -c 20 -q -t set,get -P 1000
SET: 256410.25 requests per second
GET: 232558.14 requests per second

> redis-benchmark -p 8000 -n 10000 -c 20 -q -t set,get -P 1000
SET: 147058.83 requests per second
GET: 151515.16 requests per second

> redis-benchmark -p 9000 -n 10000 -c 20 -q -t set,get -P 1000
SET: 129870.13 requests per second
GET: 100000.00 requests per second
We are increasing it more and we see that with such size of Pipeline it is Redis itself giving up.
> redis-benchmark -p 6380 -n 10000 -c 20 -q -t set,get -P 10000
SET: 17421.60 requests per second
GET: 21834.06 requests per second
While RedisSharding, judging from the above information, is perfectly fine:
> redis-benchmark -p 8000 -n 10000 -c 20 -q -t set,get -P 10000
SET: 13280.21 requests per second
GET: 19011.41 requests per second
What can't be said of nutcracker.
> redis-benchmark -p 9000 -n 10000 -c 20 -q -t set,get -P 10000
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
SET: 17064.85 requests per second
zmalloc: Out of memory trying to allocate 335799 bytes
Abort



> nutcracker-0.2.2/src/nutcracker -c twemproxy/nutcracker0.yml
[Fri Jan 25 09:36:19 2013] nc.c:177 nutcracker-0.2.2 started on pid 19962
[Fri Jan 25 09:36:19 2013] nc.c:181 run, rabbit run / dig that hole, forget the sun / and when at last the work is done / don't sit down / it's time to dig another one
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 8 failed: Connection reset by peer
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 9 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 10 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 11 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 12 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 13 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 14 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 15 failed: Broken pipe

Redis: SET vs MSET; Sharding: twemproxy vs RedisSharding

It's logical that one MSET command with multiple keys is faster than multiple SET commands. The question is: how much faster when using real clients? What is more rational for the data sharding? twemproxy or RedisSharding? The first one is very fast, the second one supports MSET, MGET commands properly.

Let's take these clients: http://search.cpan.org/~melo/Redis-1.955 (Simple client) and http://search.cpan.org/~dgl/AnyEvent-Redis-0.24 (Event client).

"No Mutly Keys" test was run SET command for 10 keys, then GET for this keys, and then DEL.
"Yes Mutly Keys" test was run MSET with 10 keys, then MGET with this keys, and then DEL.
This series of commands was run 1000 time for each test.

Version:
Redis         - 2.4.15 (http://redis.io)
nutcracker    - 0.2.2 (http://github.com/twitter/twemproxy)
RedisSharding - 1.0 (http://github.com/kni/redis-sharding-hs-strict)
twemproxy and RedisSharding used 4 Redis nodes.
Clients, Redis nodes, twemproxy, RedisSharding were run on one computer.
The comparison includes the data for the isolated Redis server.

Average time of executed test scripts:
Client | Mutly Keys | Redis | twemproxy   | RedisSharding
---------------------------------------------------------
Simple |   No       | 02.72 | 03.93       | 05.69
Event  |   No       | 03.65 | 03.54       | 03.54
Simple |   Yes      | 00.86 | no support* | 01.34
Event  |   Yes      | 00.61 | no support* | 00.75
* - twemproxy supports MSET basing on the hash tag only, while we are interested in proper full support! https://github.com/twitter/twemproxy/issues/51#issuecomment-12257220

And now, let's take the faster client (http://hackage.haskell.org/package/hedis):

Keys | Mutly Keys | Redis | twemproxy   | RedisSharding
-------------------------------------------------------
 10  |   No       | 00.08 | 00.13       | 00.26
 10  |   Yes      | 00.02 | no support* | 00.14
100  |   No       | 00.78 | 01.34       | 02.56
100  |   Yes      | 00.30 | no support* | 00.90 !!!
Conclusions:
If you don't have to use Mutly Keys commands, you can choose twemproxy for sharding.
However, if you have an opportunity to use MSET and MGET, it's worth trying RedisSharding.