本文共 12095 字,大约阅读时间需要 40 分钟。
Redis有多快?
Redis 包含了工具程序redis-benchmark,它可以模拟运行命令,相当于模拟N个客户端同时发送总数M个查询(和apache的ab工具程序类似)。下面是在linux系统上执行benchemark后的完整输出,支持的选项如下:
Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]
-h <hostname> Server hostname (default 127.0.0.1)
-p <port> Server port (default 6379) -s <socket> Server socket (overrides host and port) -a <password> Password for Redis Auth -c <clients> Number of parallel connections (default 50) -n <requests> Total number of requests (default 100000) -d <size> Data size of SET/GET value in bytes (default 2) -dbnum <db> SELECT the specified db number (default 0) -k <boolean> 1=keep alive 0=reconnect (default 1) -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD Using this option the benchmark will expand the string __rand_int__ inside an argument with a 12 digits number in the specified range from 0 to keyspacelen-1. The substitution changes every time a command is executed. Default tests use this to hit random keys in the specified range. -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline). -q Quiet. Just show query/sec values –csv Output in CSV format -l Loop. Run the tests forever -t <tests> Only run the comma separated list of tests. The test names are the same as the ones produced as output. -I Idle mode. Just open N idle connections and wait.
启动benchmark之前你得有个正在运行的Redis实例。运行benchmark典型示例如下:
redis-benchmark -q -n 100000
使用这个工具比较简单,你也可以实现你自己的benchmark,但是有些坑不要踩。
只运行一部分测试
不是每次运行redis-benchmark时,都要运行所有默认的测试。选择一部分来测试,只要简单地使用-t选项,如下:
$ redis-benchmark -t set,lpush -n 100000 -q
SET: 74239.05 requests per second LPUSH: 79239.30 requests per second
上面的例子中,在安静模式下(参见-q选项),我们只测试了SET和LPUSH命令。也可以直接给benchmark指定命令,如下:
$ redis-benchmark -n 100000 -q script load “redis.call(‘set’,’foo’,’bar’)”
script load redis.call(‘set’,’foo’,’bar’): 69881.20 requests per second
选择key空间的大小
默认情况下,benchmark只针对单一个key测试。在人为测试环境和真实使用环境下,Redis表现出来的差别不是很大,因为它是一个内存系统。但如果使用更大范围的key,则可以测试缓存命中,并模拟一个更真实的工作负载。
使用-r选项就可以达到此目的,比如我想运行一百万次SET操作,每次操作从10万个key里面随机选一个,可以使用下面的命令:
$ redis-cli flushall
OK$ redis-benchmark -t set -r 100000 -n 1000000
====== SET ====== 1000000 requests completed in 13.86 seconds 50 parallel clients 3 bytes payload keep alive: 199.76% `<=` 1 milliseconds
99.98% `<=` 2 milliseconds 100.00% `<=` 3 milliseconds 100.00% `<=` 3 milliseconds 72144.87 requests per second$ redis-cli dbsize
(integer) 99993
管道化
默认情况下,每个模拟的客户端(如果不指定选项-c,benchmark模拟50个客户端)需要等待,只有收到前一条命令的响应之后才会发送下一条命令。这意味着,客户端发送的每条命令,服务器都需要一次read调用才能获得。当然,等待时间也得算上RTT的时间。
Redis支持即管道化,因此是可以一次发送多条命令的。现实中的应用经常用到这个特性。Redis在管道化模式下能够大幅提高服务器的每秒操作数。
下面的例子中,使用由16条命令的组成的管道,在一台MacBook Air 11″上运行benchmark:
$ redis-benchmark -n 1000000 -t set,get -P 16 -q
SET: 403063.28 requests per second GET: 508388.41 requests per second
使用管道化可以大大地提高性能。
一些坑和误区
第一点很明显: benmark测试的黄金法则是只比较相互有可比性的东西。例如基于相同的工作负载比较不同版本的Redis。或者使用相同版本的Redis,但是使用不同的选项运行。如果你打算把Redis和其他什么东西进行比较,评估和考虑他们功能上和技术上的差异非常重要。
一种常见的误区是redis-benchmark被设计成使得redis性能看上去更好,redis-benchmark的吞吐量似乎有点假,而不是由真实应用输出的。这种认识实际是不对的。
要获得和评估一个redis实例在给定硬件上的性能,使用redis-benchmark程序是一种快速且有用的方式。然而,默认情况下,redis-benmark并不能测出一个redis实例所能承受的最大吞吐量。实际上,通过使用管道化和快速客户端(hiredis),写一个吞吐量比redis-benchmark更高的程序是相当容易的。redis-benchmark的默认行为仅仅是利用并发性来达成吞吐量(即创建了好几个连接到服务器),并没有使用管道化或者任何并行操作(每个连接上最多一条等待处理的查询,也没有多线程)。
要使用管道化运行一个benchmark获得更大吞吐量,你需要加上-P选项。注意,这样比较切实,因为很多基于redis的应用积极使用管道化来提高性能。
最后,进行多个数据存储比较时,benchmark应该处在相同的工作模式下,使用相同的操作。把redis-benchmark的测试结果和其他benchmark程序的结果比较和推断,没什么意义。
例如,可以对redis和单线程模式的memcached基于GET/SET操作进行比较。两个都是内存数据存储,协议层几乎是一样的工作方式。如果各自的benchmark程序以同样的方式(管道化)汇集多条查询,并使用差不多的连接数,这样的比较才有意义。
Redis (antirez) 和memcached (dormando) 的开发人员之间的对话就是最好的例子。
(校对注:欢迎在ifeve.com翻译发表此文)
(校对注:不能访问)
从最后的结果可以看到,考虑所有技术相关的方面,这两种解决方案之间的差异并没有大得让人出乎意料。注意,在这些性能测试之后,redis和memcached已经又进一步优化了。
最后,在测试那些高效的服务器时(redis和memcache肯定属于这一类),其实很难让服务达到满负荷。有时性瓶颈在客户端这边,而不是服务器那边。这种情况下,要让服务器达到最大吞吐量,客户端(也就是benchmark程序本身)必须得修复或扩展。
影响redis性能的因素
有多种因素会直接影响redis的性能。这里,我们分析一些,因为它们可以改变所有benchmark测试的结果。但请注意,一个典型的redis实例,运行在低速、未调优过的系统上,提供的性能对大多数应用来说也是够好的。
其他要考虑的事情
benchmark的一个重要目的是得到可重复的结果,这样才能和其他测试的结果进行比较。
不同虚拟机和裸机上的benchmark结果
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (with pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -q
SET: 552028.75 requests per second GET: 707463.75 requests per second LPUSH: 767459.75 requests per second LPOP: 770119.38 requests per second
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second GET: 123601.76 requests per second LPUSH: 136752.14 requests per second LPOP: 132424.03 requests per second
Linode 2048 instance (with pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q -P 16
SET: 195503.42 requests per second GET: 250187.64 requests per second LPUSH: 230547.55 requests per second LPOP: 250815.16 requests per second
Linode 2048 instance (without pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 35001.75 requests per second GET: 37481.26 requests per second LPUSH: 36968.58 requests per second LPOP: 35186.49 requests per second
更多未使用管道化的详细测试
$ redis-benchmark -n 100000
====== SET ======
100007 requests completed in 0.88 seconds 50 parallel clients 3 bytes payload keep alive: 158.50% <= 0 milliseconds
99.17% <= 1 milliseconds 99.58% <= 2 milliseconds 99.85% <= 3 milliseconds 99.90% <= 6 milliseconds 100.00% <= 9 milliseconds 114293.71 requests per second====== GET ======
100000 requests completed in 1.23 seconds 50 parallel clients 3 bytes payload keep alive: 143.12% <= 0 milliseconds
96.82% <= 1 milliseconds 98.62% <= 2 milliseconds 100.00% <= 3 milliseconds 81234.77 requests per second====== INCR ======
100018 requests completed in 1.46 seconds 50 parallel clients 3 bytes payload keep alive: 132.32% <= 0 milliseconds
96.67% <= 1 milliseconds 99.14% <= 2 milliseconds 99.83% <= 3 milliseconds 99.88% <= 4 milliseconds 99.89% <= 5 milliseconds 99.96% <= 9 milliseconds 100.00% <= 18 milliseconds 68458.59 requests per second====== LPUSH ======
100004 requests completed in 1.14 seconds 50 parallel clients 3 bytes payload keep alive: 162.27% <= 0 milliseconds
99.74% <= 1 milliseconds 99.85% <= 2 milliseconds 99.86% <= 3 milliseconds 99.89% <= 5 milliseconds 99.93% <= 7 milliseconds 99.96% <= 9 milliseconds 100.00% <= 22 milliseconds 100.00% <= 208 milliseconds 88109.25 requests per second====== LPOP ======
100001 requests completed in 1.39 seconds 50 parallel clients 3 bytes payload keep alive: 154.83% <= 0 milliseconds
97.34% <= 1 milliseconds 99.95% <= 2 milliseconds 99.96% <= 3 milliseconds 99.96% <= 4 milliseconds 100.00% <= 9 milliseconds 100.00% <= 208 milliseconds 71994.96 requests per second
注意:修改包的载荷分别为256,1024,4096字节,并没有明显改变结果数字(但应答包被合在一起直到1024字节,因此大数据包时GET会慢些)。客户端数量从50变到256时,结果数字是一样的。只有10个客户端时,则慢一点。
不同的系统上会有不同结果。比如一个低配主机,CPU是主频为1.66GHz 的intel coreT5500,运行着linux 2.6,测试结果如下:
$ ./redis-benchmark -q -n 100000
SET: 53684.38 requests per second GET: 45497.73 requests per second INCR: 39370.47 requests per second LPUSH: 34803.41 requests per second LPOP: 37367.20 requests per second
另一个CPU为2.5GHz Xeon L5420的64位主机上,结果如下:
$ ./redis-benchmark -q -n 100000
PING: 111731.84 requests per second SET: 108114.59 requests per second GET: 98717.67 requests per second INCR: 95241.91 requests per second LPUSH: 104712.05 requests per second LPOP: 93722.59 requests per second
在优化过的高端服务硬件上的测试结果示例
使用UNIX域套接字:
$ numactl -C 6 ./redis-benchmark -q -n 100000 -s /tmp/redis.sock -d 256
PING (inline): 200803.22 requests per second PING: 200803.22 requests per second MSET (10 keys): 78064.01 requests per second SET: 198412.69 requests per second GET: 198019.80 requests per second INCR: 200400.80 requests per second LPUSH: 200000.00 requests per second LPOP: 198019.80 requests per second SADD: 203665.98 requests per second SPOP: 200803.22 requests per second LPUSH (again, in order to bench LRANGE): 200000.00 requests per second LRANGE (first 100 elements): 42123.00 requests per second LRANGE (first 300 elements): 15015.02 requests per second LRANGE (first 450 elements): 10159.50 requests per second LRANGE (first 600 elements): 7548.31 requests per second
使用TCP回路:
$ numactl -C 6 ./redis-benchmark -q -n 100000 -d 256
PING (inline): 145137.88 requests per second PING: 144717.80 requests per second MSET (10 keys): 65487.89 requests per second SET: 142653.36 requests per second GET: 142450.14 requests per second INCR: 143061.52 requests per second LPUSH: 144092.22 requests per second LPOP: 142247.52 requests per second SADD: 144717.80 requests per second SPOP: 143678.17 requests per second LPUSH (again, in order to bench LRANGE): 143061.52 requests per second LRANGE (first 100 elements): 29577.05 requests per second LRANGE (first 300 elements): 10431.88 requests per second LRANGE (first 450 elements): 7010.66 requests per second LRANGE (first 600 elements): 5296.61 requests per second