При этом есть нюанс: курсор - это целое произвольной точности.
То есть, если клиенты оперируют с курсором как со строкой или числом произвольной точности, то они будут работать с командой SCAN, иначе нет.
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
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
Requests per second | | threaded ---------------------------- GHC 7.8.1 | 11560 | 12853 GHC 7.6.1 | 11210 | 7686
Requests per second | | threaded ---------------------------- GHC 7.8.1 | 16393 | 17605 GHC 7.6.1 | 16583 | 9354
... +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
-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
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 secondAnd 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 secondHowever, RedisSharding uses all the 4 core sets while the twemproxy is one-thread.
> 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 secondBut 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 secondWe 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 secondWhile 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 secondWhat 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
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.
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: