add benchmark with redis

This commit is contained in:
fortrue 2018-01-25 16:57:18 +08:00
parent b9a468dc97
commit 55cc0dccfd
13 changed files with 100 additions and 0 deletions

View File

@ -0,0 +1,100 @@
从我们的直观感受来讲,对于任何服务,只要在中间增加了一层,肯定会对服务性能造成影响。那么到底会影响什么呢?在考察一个服务性能的时候,有两个最重要的指标,那就是吞吐和延迟。吞吐定义为服务端单位时间内能处理的请求数,延迟定义为客户端从发出请求到收到请求的耗时。中间环节的引入我们首先想到的就是那会增加处理时间,这就会增加服务的延迟,于是顺便我们也会认为吞吐也会下降。从单个用户的角度来讲,事实确实如此,我完成一个请求的时间增加了,那么我单位时间内所能完成的请求量必定就减少了。然而站在服务端的角度来看,虽然单个请求的处理时间增加了,但是总的吞吐就一定会减少吗?
接下来我们就来对redis来进行一系列的测试利用redis自带的redis-benchmark分别对set和get命令单个发送和批量发送直连redis和连接redis代理[predixy](https://github.com/joyieldInc/predixy)。这样组合起来总共就是八种情况。redis-benchmark、redis是单线程的predixy支持多线程但是我们也只运行一个线程这三个程序都运行在一台机器上。
|项目|内容|
|---|---|
|CPU|AMD Ryzen 7 1700X Eight-Core Processor 3.775GHz|
|内存|16GB DDR4 3000
|OS|x86_64 GNU/Linux 4.10.0-42-generic #46~16.04.1-Ubuntu
|redis|版本3.2.9端口7200
|predixy|版本1.0.2端口7600
八个测试命令
|测试命令|命令行|
|--|--|
|redis set|redis-benchmark -h xxx -p 7200 -t set -r 3000 -n 40000000
|predixy set|redis-benchmark -h xxx -p 7600 -t set -r 3000 -n 40000000
|redis get|redis-benchmark -h xxx -p 7200 -t get -r 3000 -n 40000000
|predixy get|redis-benchmark -h xxx -p 7600 -t get -r 3000 -n 40000000
|redis 批量set|redis-benchmark -h xxx -p 7200 -t set -r 3000 -n 180000000 -P 20
|predixy 批量set|redis-benchmark -h xxx -p 7600 -t set -r 3000 -n 180000000 -P 20
|redis 批量get|redis-benchmark -h xxx -p 7200 -t get -r 3000 -n 420000000 -P 20
|predixy 批量get|redis-benchmark -h xxx -p 7600 -t get -r 3000 -n 220000000 -P 20
以上8条命令采取redis-benchmark默认的50个并发连接数据大小为2字节指定3000个key批量测试时一次发送20个请求。依次间隔2分钟执行以上命令每一个测试完成时间大约4分钟。最后得到下图的总体结果
![整体结果](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/overview.png)
眼花缭乱是不是左边的纵轴表示CPU使用率右边的纵轴表示吞吐。其中redis used表示redis总的CPU使用率redis user表示redis CPU用户态使用率redis sys表示redis CPU内核态使用率其它类推。先别担心分不清里面的内容下面我们会一一标出数值来。在这图中总共可以看出有八个凸起依次对应我们上面提到的八个测试命令。
1 redis set测试
![redis_set](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/redis_set.png)
2 predixy set测试
![predixy_set](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/predixy_set.png)
3 redis get测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/redis_get.png)
4 predixy get测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/predixy_get.png)
5 redis 批量set测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/redis_pipeline_set.png)
6 predixy 批量set测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/predixy_pipeline_set.png)
7 redis 批量get测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/redis_pipeline_get.png)
8 predixy 批量get测试
![这里写图片描述](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/predixy_pipeline_get.png)
图片还是不方便看,我们总结为表格:
|测试\指标|redis used|redis user|redis sys|predixy used|predixy user|predixy sys|redis qps|predixy qps|
|--|--|--|--|--|--|--|--|--|
|redis set|0.990|0.247|0.744|0|0|0|167000|3|
|predixy set|0.475|0.313|0.162|0.986|0.252|0.734|174000|174000|
|redis get|0.922|0.180|0.742|0|0|0|163000|3|
|predixy get|0.298|0.195|0.104|0.988|0.247|0.741|172000|172000|
|redis批量set|1.006|0.796|0.21|0|0|0|782000|3|
|predixy批量set|0.998|0.940|0.058|0.796|0.539|0.256|724000|724000|
|redis批量get|1|0.688|0.312|0|0|0|1708000|3|
|predixy批量get|0.596|0.582|0.014|0.999|0.637|0.362|935000|935000|
看到前四个的结果如果感到惊讶不用怀疑是自己看错了或者是测试结果有问题这个结果是无误的。根据这个结果那么可以回答我们最初提出的疑问增加了代理之后并不一定会降低服务整体的吞吐虽然benchmark并不是我们的实际应用但是redis的大部分应用场景都是这样的并发的接受众多客户端的请求处理然后返回。
为什么会是这样的结果看到这个结果后我们肯定想知道原因这好像跟我们的想象不太一样。要分析这个问题我们还是从测试的数据来入手首先看测试1的数据redis的CPU使用率几乎已经达到了1对于单线程程序来说这意味着CPU已经跑满了性能已经达到了极限不可能再提高了然而这时redis的吞吐却只有167000。测试2的redis吞吐都比它高并且我们明显能看出测试2里redis的CPU使用率还不如测试1的高测试2里redis CPU使用率只有0.475。为什么CPU使用率降低了吞吐反而却还高了呢仔细对比一下两个测试的数据可以发现在测试1里redis的CPU大部分都花在了内核态高达0.744而用户态只有0.247CPU运行在内核态时虽然我们不能称之为瞎忙活但是却无助于提升程序的性能只有CPU运行在用户态才可能提升我们的程序性能相比测试1测试2的redis用户态CPU使用率提高到了0.313而内核态CPU则大幅下降至0.162。这也就解释了为什么测试2的吞吐比测试1还要高。当然了我们还是要继续刨根问底为什么测试2里经过一层代理predixy后redis的CPU使用情况发生变化了呢这是因为redis接受一个连接批量的发送命令过来处理也就是redis里所谓的pipeline。而predixy正是利用这一特性predixy与redis之间只有一个连接大多数情况下predixy在收到客户端的请求后会将它们批量的通过这个连接发送给redis处理这样一来就大大降低了redis用于网络IO操作的开销而这一部分开销基本都是花费在内核态。
对比测试1和测试2引入predixy不仅直接提高了吞吐还带来一个好处就是redis的CPU使用率只有一半不到了这也就意味着如果我再把剩下的这一半CPU用起来还可以得到更高的吞吐而如果没有predixy这样一层的话测试1结果告诉我们redis的CPU利用率已经到头了吞吐已经不可能再提高。
测试3和测试4说明的问题与测试1和测试2一样如果我只做了这四个测试那么看起来好像代理的引入完全有助于提升我们的吞吐嘛。正如上面所分析的那样predixy提升吞吐的原因是因为采用了批量发送手段。那么如果客户端的使用场景就是批量发送命令那结果会如何呢
于是有了后面四个测试后面四个测试给我们的直接感受就是太震撼了吞吐直接提升几倍甚至10倍其实也正是因为redis批量模式下性能非常强悍才使得predixy在单命令情况下改进吞吐成为可能。当然到了批量模式从测试结果看predixy使得服务的吞吐下降了。
具体到批量set时直连redis和通过predixyredis的CPU使用率都满了虽然采用predixy使得redis的用户态CPU从0.796提高到了0.940但是吞吐却不升反降从782000到724000大约下降了7.4%。至于为什么用户态CPU利用率提高了吞吐却下降了要想知道原因就需要分析redis本身的实现这里我们就不做详细探讨。可以做一个粗糙的解释在redis CPU跑满的情况下不同的负载情况会使得用户态和内核态的使用率不同而这其中有一种分配情况会是吞吐最大而用户态使用率高于或者低于这种情况时都会出现吞吐下降的情况。
再来看批量get直连redis时吞吐高达1708000而通过predixy的话就只有935000了下降了45%就跟纳了个人所得税上限一般。看到这刚刚对predixy建立的美好形象是不是又突然觉得要坍塌了先别急再看看其它指标直连redis时redis CPU跑满而通过predixy时redis CPU只用了0.596也就是说redis还有四成的CPU等待我们去压榨。
写到这既然上面提到批量get时通过predixy的话redis并未发挥出全部功力于是就想着如果全部发挥出来会是什么情况呢我们继续增加两个测试既然单个predixy在批量的情况下造成了吞吐下降但是给我们带来了一个好处是redis还可以提升的余地那么我们就增加predixy的处理能力。因此我们把predixy改为三线程再来跑一遍测试6和测试8。
两个测试的整体结果如下。
![mt_overview](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/mt_overview.png)
三线程predixy批量set
![mt_predixy_set](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/mt_predixy_pipeline_set.png)
三线程predixy批量get
![mt_predixy_get](https://github.com/joyieldInc/predixy/blob/master/doc/bench/redis/mt_predixy_pipeline_get.png)
|测试\指标|redis used|redis user|redis sys|predixy used|predixy user|predixy sys|redis qps|predixy qps|
|--|--|--|--|--|--|--|--|
|predixy pipeline set|1.01|0.93|0.07|1.37|0.97|0.41|762000|762000|
|predixy pipeline get|0.93|0.85|0.08|2.57|1.85|0.72|1718000|1718000|
原本在单线程predixy的批量set测试中predixy和redis的CPU都已经跑满了我们觉得吞吐已经达到了极限但是实际结果显示在三线程predixy的批量set测试中吞吐还是提高了从原来的724000到现在的76200与直连的782000只有2.5%的差距。多线程和单线程的主要差别在于单线程时predixy与redis只有一个连接而三线程时有三个连接。
而对于三线程predixy的批量get测试不出我们所料的吞吐得到了极大的提升从之前的935000直接飙到1718000已经超过了直连的1708000。
最后我们来总结一下我们整个测试的场景比较简单只是单纯的set、get测试并且数据大小为默认的2字节实际的redis应用场景远比这复杂的多。但是测试结果的数据依旧可以给我们一些结论。代理的引入并不一定会降低服务的吞吐实际上根据服务的负载情况有时候引入代理反而可以提升整个服务的吞吐如果我们不计较代理本身所消耗的资源那么引入代理几乎总是一个好的选择。根据我们上面的分析一个最简单实用的判断原则看看你的redis CPU使用情况如果花费了太多时间在内核态那么考虑引入代理吧。

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB