热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

引入redis代理是否一定会降低redis服务性能?

从我们的直观感受来讲,对于任何服务,只要在中间增加了一层,肯定会对服务性能造成影响。那么到底会影响什么呢?在考察一个服务性能的时候,有两个最重要的指标,那就是吞吐和延迟。吞吐定义为

从我们的直观感受来讲,对于任何服务,只要在中间增加了一层,肯定会对服务性能造成影响。那么到底会影响什么呢?在考察一个服务性能的时候,有两个最重要的指标,那就是吞吐和延迟。吞吐定义为服务端单位时间内能处理的请求数,延迟定义为客户端从发出请求到收到请求的耗时。中间环节的引入我们首先想到的就是那会增加处理时间,这就会增加服务的延迟,于是顺便我们也会认为吞吐也会下降。从单个用户的角度来讲,事实确实如此,我完成一个请求的时间增加了,那么我单位时间内所能完成的请求量必定就减少了。然而站在服务端的角度来看,虽然单个请求的处理时间增加了,但是总的吞吐就一定会减少吗?

接下来我们就来对redis来进行一系列的测试,利用redis自带的redis-benchmark,分别对set和get命令;单个发送和批量发送;直连redis和连接redis代理predixy(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

八个测试命令

1. redis set

redis-benchmark -h xxx -p 7200 -t set -r
3000 -n 40000000

2. predixy set

redis-benchmark -h xxx -p 7600 -t set -r
3000 -n 40000000

3. redis get

redis-benchmark -h xxx -p 7200 -t get -r
3000 -n 40000000

4. predixy get

redis-benchmark -h xxx -p 7600 -t get -r
3000 -n 40000000

5. redis 批量set

redis-benchmark -h xxx -p 7200 -t set -r
3000 -n 180000000 -P 20

6. predixy 批量set

redis-benchmark -h xxx -p 7600 -t set -r
3000 -n 180000000 -P 20

7. redis 批量get

redis-benchmark -h xxx -p 7200 -t get -r
3000 -n 420000000 -P 20

8. 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分钟。最后得到下图的总体结果

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

眼花缭乱是不是?左边的纵轴表示CPU使用率,右边的纵轴表示吞吐。其中redis used表示redis总的CPU使用率,redis user表示redis CPU用户态使用率,redis sys表示redis CPU内核态使用率,其它类推。先别担心分不清里面的内容,下面我们会一一标出数值来。在这图中总共可以看出有八个凸起,依次对应我们上面提到的八个测试命令。

1、 redis set测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

2、 predixy set测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

3、 redis get测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

4、 predixy get测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

5、 redis批量set测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

6、 predixy批量set测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

7、 redis批量get测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

8、 predixy批量get测试

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

看到前四个的结果如果感到惊讶不用怀疑是自己看错了或者是测试结果有问题,这个结果是无误的。根据这个结果,那么可以回答我们最初提出的疑问,增加了代理之后并不一定会降低服务整体的吞吐!虽然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.247,CPU运行在内核态时虽然我们不能称之为瞎忙活,但是却无助于提升程序的性能,只有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和通过predixy,redis的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。

两个测试的整体结果如下。

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

三线程predixy批量set

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

三线程predixy批量get

《引入redis代理是否一定会降低redis服务性能?》
《引入redis代理是否一定会降低redis服务性能?》

原本在单线程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使用情况,如果花费了太多时间在内核态,那么考虑引入代理吧。


推荐阅读
  • 本文详细介绍了在 Ubuntu 系统上搭建 Hadoop 集群时遇到的 SSH 密钥认证问题及其解决方案。通过本文,读者可以了解如何在多台虚拟机之间实现无密码 SSH 登录,从而顺利启动 Hadoop 集群。 ... [详细]
  • 面试题总结_2019年全网最热门的123个Java并发面试题总结
    面试题总结_2019年全网最热门的123个Java并发面试题总结 ... [详细]
  • 在 CentOS 7 系统中安装 Scrapy 时遇到了一些挑战。尽管 Scrapy 在 Ubuntu 上安装简便,但在 CentOS 7 上需要额外的配置和步骤。本文总结了常见问题及其解决方案,帮助用户顺利安装并使用 Scrapy 进行网络爬虫开发。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在嵌入式Linux系统中,性能低下通常由CPU、内存和I/O三个关键因素引起。为了有效提升系统性能,首先需要识别并定位性能瓶颈。通过综合分析这些瓶颈,可以采取针对性的优化措施,如调整内核参数、优化算法和改进数据结构等,从而显著提高系统的整体性能。 ... [详细]
  • 可参照github代码:https:github.comrabbitmqrabbitmq-tutorialsblobmasterjavaEmitLogTopic.ja ... [详细]
  • Linux 防火墙与端口管理必备命令
    在使用 Linux 系统进行服务部署和问题排查时,防火墙和端口管理是不可或缺的操作。本文将详细介绍如何查看防火墙状态、端口占用情况,以及如何开放和关闭端口,帮助初学者更好地掌握这些技能。 ... [详细]
  • 本文探讨了 TypeScript 中泛型的重要性和应用场景,通过多个实例详细解析了泛型如何提升代码的复用性和类型安全性。 ... [详细]
  • 本文详细介绍了 Java 网站开发的相关资源和步骤,包括常用网站、开发环境和框架选择。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • Python多线程详解与示例
    本文介绍了Python中的多线程编程,包括僵尸进程和孤儿进程的概念,并提供了具体的代码示例。同时,详细解释了0号进程和1号进程在系统中的作用。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 多线程基础概览
    本文探讨了多线程的起源及其在现代编程中的重要性。线程的引入是为了增强进程的稳定性,确保一个进程的崩溃不会影响其他进程。而进程的存在则是为了保障操作系统的稳定运行,防止单一应用程序的错误导致整个系统的崩溃。线程作为进程的逻辑单元,多个线程共享同一CPU,需要合理调度以避免资源竞争。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有