Redis6.0多线程性能测试结果及分析

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 单线程的Redis一向以简洁高效著称,但也有其阿喀琉斯之踵:阻塞!单个线程在最容易产生瓶颈的网络读写(Redis的key)请求完成之前,其他所有请求都将会被阻塞,严重影响其效率,因此Redis的多线程呼声就越来越高。由于是基于内存的操作延迟非常低,所以即便是单线程模式下CPU资源也不会是的瓶颈。最容易出现瓶颈的还是网络IO操作。在Redis 6.0开始支持多线程之后,所谓的多线程也只是socket层面的多线程,核心的内存读写还是单线程模式。

单线程的Redis一向以简洁高效著称,但也有其阿喀琉斯之踵:阻塞!单个线程在最容易产生瓶颈的网络读写(Redis的key)请求完成之前,其他所有请求都将会被阻塞,严重影响其效率,因此Redis的多线程呼声就越来越高。由于是基于内存的操作延迟非常低,所以即便是单线程模式下CPU资源也不会是的瓶颈。最容易出现瓶颈的还是网络IO操作。在Redis 6.0开始支持多线程之后,所谓的多线程也只是socket层面的多线程,核心的内存读写还是单线程模式。

弄清楚了多线程的本质之后,就会有一系列的问题,多线程会比单线程有多大的提升?设置多少个线程合适?见一些大神测试过,其结果也非常理想,但只是看看也不太过瘾,决定一试为快,本文将对Redis的多线程进行一个粗浅的测试验证。同时需要思考另外一个问题:面对多线程版本的Redis,和Redis cluster,该如何选择?


多线程Redis


redis 6.0 的“多线程”特性让很多标题党高潮连连,参考图片源自于:美图技术团队侵删,核心的线程(Execute Command)还是单线程,多线程是指网络IO(socket)读写的多线程化。

如下图所示,读写网络socket中的数据是可以用多个线程,所以Redis的多线程也叫做io thread,相关参数:“io-threads”。另一个参数是io-threads-do-reads,这里涉及另外一个细节:多线程IO主要用在请求完成后返回结果集的过程,也就是socket写操作,至于读socket,单线程本身采用的多路IO复用技术,也不会产生瓶颈,因此Redis给出了一个io-threads-do-reads 参数,决定读socket的时候是否启用多线程。其实io-threads-do-reads是否启用,对性能的影响并不大,最后会做一个验证。


image.png


测试策略


本机配置:centos 7,16C+32GB内存image.png

Redis版本

image.png

下面分别以1线程,2线程,4线程,6线程,8线程,10线程的配置下,同时为避免网络延迟带来的影响,redis-benchmark在Redis实例本地,测试Redis的get和set性能。


翻车


整个测试开始之前,经历了两次翻车才得以继续


翻车现场1

centos 7上默认的gcc是4.*版本,无法编译Redis 6.0,所以需要升级gcc,因为本机不支持yum安装,参考这里使用源码包安装,gcc编译的时候那个酸爽,本机16C+32GB内存的环境下,因为缺少某些依赖包,导致失败了几次,最终编译成功的一次,花了大概1个小时10分钟

image.png

翻车现场2

没有认真读配置文件中的说明,设置io-threads后,重启Redis服务后,上来就用redis-benchmark直接怼,其结果跟单线程差不多,令人大跌眼镜。

最后还是在原始配置文件发现了这段话:

If you want to test the Redis speedup using redis-benchmark, make sure you also run the benchmark itself in threaded mode, using the --threads option to match the number of Redis threads, otherwise you'll not be able to notice the improvements.

意思是必须在redis-benchmark设置--threads参数,并且要match Redis中的线程设置,--threads参数是redis 6.0后新增的一个参数。只有加上--threads这个参数才能体现出来多线程Redis的效率。

image.png


关于Thread IO的说明


经历了第二次翻车之后决定好好看一看redis.conf中关于thread io的注释信息

image.png

大概意思如下:

大多数情况下redis是以单线程的方式运行的,然而,有一些线程操作,如断开链接,耗时的I/O操作(bgsave,expired key清理之类的操作)和其他任务是在side线程(主线程fork出来的子线程)中执行的。

现在也可以在不同的I/O线程中处理Redis客户端socket读和写。由于写入(指socket写入)速度非常慢,Redis用户通常使用pipelining来提高Redis在单核上的性能,并使用多个实例的方式来扩容。使用I/O线程可以很容易地提升Redis在socket读写上的性能,而无需求助于管道或实例的分片。

Redis 6.0中默认情况下多线程被是被禁用的,建议至少有4个或更多核的机器中启用多线程,且至少留下1备用核。使用超过8个线程不大可能有太大帮助。

由于Redis实例能够充分利用CPU资源(译者注:意思是即便是单线程下,CPU并不是瓶颈),多线程I/O只有在你确实有性能问题的情况下才能提升运行效率,否则就没有必要使用这个特性。

如果你有一个4核的服务器,尝试使用2或3个I/O线程,如果是8核,尝试使用6个线程。要启用多线程I/O,请使用以下配置参数:io-threads 4

设置io-threads为1会像传统的redis一样启用单个主线程,当I/O threads被启用之后,仅仅支持写操作(译者注:指的是socket的写操作,socket的读操作使用多路io复用技术,本身也不是瓶颈)即IO线程调用syscall并将客户端缓冲区传输到socket。但是,也可以启用读写线程,使用以下配置指令进行协议解析,方法是将其设置为“yes”:io-threads-do-reads no

通常情况下,threading reads线程对性能的提升帮助并不大

注1:此配置指令不能在运行时通过配置集进行更改,只能在修改配置文件之后重启。启用SSL时,当前此特性也无效。

注2:如果你想用Redis-benchmark测试Redis的性能,务必以threaded mode的方式运行Redis-benchmark,使用--threads选项来匹配Redis线程的数量,否则无法观察到测试结果的提升。


测试结果及分析


如下是不同线程requests per second测试结果的横向对比

image.png

从中可以看到:

1,1个线程,也就是传统的单线程模式,get 操作的QPS可以达到9W左右

2,2个线程,get 操作的QPS可以达到18W左右,相比单线程有100%+的提升

3,4个线程,与2线程相比,会有20%左右的提高,但是已经没有从1个线程到2个线程翻一倍的提升了

4,6个线程,与4线程相比,没有明显的提升

5,8个线程,与4线程和6线程相比,8线程下大概有10%的提升

6,10个线程,相比效率最高的8线程,此时性能反倒是开始下降了,与4线程或者6线程的效率相当

因此在本机环境下,io-threads 4设置成2或者4个都ok,最多不超过8个,超出后性能反而会下降,同时也不能超出cpu的个数,正如配置文件中注释中说的,至少要留出一个CPU。

如下是不同线程下10测试结果中GET和SET的requests per second 平均值对比:

image.png


关于io-threads-do-reads参数


上文提到过io-threads-do-reads这个参数,它是决定socket读操作是否开启多线程,Redis的socket读操作采用多路IO复用技术,本身不会成为瓶颈,所以这个参数对多线程下测试影响比较小。依旧参考这里的这个图 侵删,这个io-threads-do-reads在笔者理解起来,就是socket读的线程,是否开启影响不大。

image.png

参考如下截图,在开启了两个线程的情况下,分别开启和禁用io-threads-do-reads,从整体上看,性能影响差别很小。当然专业的大神可以从源码的角度去分析。

io-threads为2,且启动io-threads-do-reads

image.png

io-threads为2,且禁动io-threads-do-reads

image.png


多线程版本的Redis和Redis Cluster的选择


redis集群有两种模式:sentinel和cluster,这里暂时先不提sentinel,来思考多线程版本的Redis和Redis Cluster的选择问题。

Redis的Cluster解决的就是扩展性和单节点单线程的阻塞隐患,如果Redis支持了多线程(目前多线程的Redis最对不建议超出8个线程),在不考虑单个节点网卡瓶颈的情况下,其实这两个问题都已经解决了,单节点可以支持多线程和充分利用多核CPU,单节点可以支持到25W QPS,还要啥自行车?

同时要考虑到Redis cluster的痛点:

1,不支持multiple操作(第三方插件也不一定稳定)。

2,cluster中每个主节点要挂一个从节点,不管这个节点是不是独立的物理节点还是单机多实例中的一个节点,终究是增加了维护成本。

3,只能使用一个数据库

4,集群自身扩容、缩容带来的一系列slot迁移等性能问题,以及集群的管理问题

这些所谓的痛点也不复存在了,所以这里就面临一个重新选择的问题:是使用多线程版本的Redis,还是使用Redis cluster?这是一个需要思考的问题。


疑问


关于redis-benchmark 测试时候 ./bin/redis-benchmark -d 128 -c 200 -n 1000000 -t set -q --threads 2,涉及的两个参数-c和--threads,个人是不太理解的

-c的解释是:指定并发连接数

--threads是redis-benchmark的线程数?

对此去跟老钱(素未谋面,感谢)求证了一下,说该参数是redis-benchmark客户端的epoll,服务器端的多路IO复用原理已经看得我七荤八素了,客户端也是带epoll的,还是不太理解这两者之间的关系。


redis-benchmark测试现场


如下是redis-benchmark测试过程中部分截图

image.png

image.png

image.png

图太多了,就不一一贴上来了。


相关文章
|
监控 NoSQL 安全
如何在 Redis 中正确使用多线程?
【10月更文挑战第16天】正确使用 Redis 多线程需要综合考虑多个因素,并且需要在实践中不断摸索和总结经验。通过合理的配置和运用,多线程可以为 Redis 带来性能上的提升,同时也要注意避免可能出现的问题,以保障系统的稳定和可靠运行。
331 2
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
284 1
|
8月前
|
缓存 NoSQL 中间件
Redis的线程模型
Redis采用单线程模型确保操作的原子性,每次只执行一个操作,避免并发冲突。它通过MULTI/EXEC事务机制、Lua脚本和复合指令(如MSET、GETSET等)保证多个操作要么全成功,要么全失败,确保数据一致性。Redis事务在EXEC前失败则不执行任何操作,EXEC后失败不影响其他操作。Pipeline虽高效但不具备原子性,适合非热点时段的数据调整。Redis 7引入Function功能,支持函数复用,简化复杂业务逻辑。总结来说,Redis的单线程模型简单高效,适用于高并发场景,但仍需合理选择指令执行方式以发挥其性能优势。
216 6
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
MarS:微软开源金融市场模拟预测引擎,支持策略测试、风险管理和市场分析
MarS 是微软亚洲研究院推出的金融市场模拟预测引擎,基于生成型基础模型 LMM,支持无风险环境下的交易策略测试、风险管理和市场分析。
384 8
MarS:微软开源金融市场模拟预测引擎,支持策略测试、风险管理和市场分析
|
11月前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
156 11
|
11月前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
185 10
|
11月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
204 1
|
12月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
402 1
|
NoSQL 测试技术 Redis
Redis 性能测试
10月更文挑战第21天
246 2
|
1月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
139 6