Redis 6.0性特性: 多线程 | Java提升营

Redis 6.0性特性: 多线程

Redis 6.0.0稳定版(GA)终于发布了。这个版本提供了很多令人振奋的新特性和功能改进,比如新的网络协议RESP3、新的集群代理、ACL等,其中最受关注的应该是 多线程,带着诸多疑问,让我们一起开始《Redis 6.0新特性》。

Redis 6.0之前的版本真的是单线程吗?

Redis在处理客户端请求时,包括 获取(socket读)、解析、执行、内容返回(socket写),都是由一个顺序的串行主线程来处理的,这就是所谓的 单线程。但如果严格来说,从Redis 4.0开始就不是单线程了,除了主线程外,它还有后台线程,这些线程在处理一些比较慢的操作,比如清理脏数据、释放无用连接、删除大量keys等。

为什么Redis6.0之前一直没有使用多线程?

官方也对类似的问题做出了回应。在使用Redis时,几乎不存在CPU瓶颈,Redis主要受到内存和网络的限制。比如在普通的Linux系统上,Redis在使用pipelineing时每秒可以处理100万个请求,所以如果程序主要使用O(N)或O(log(N))级别的操作,几乎不会占用太多CPU。

并且使用单线程的话,可维护性高。多线程模型虽然在某些方面表现良好,但它会引入一些问题需要解决,比如执行顺序的不确定性,并发读写的一系列问题,增加了系统的复杂度,可能会出现线程切换甚至解锁和死锁造成的性能损失。Redis通过AE事件模型和IO多路复用技术,具有非常高的处理性能,所以不需要使用多线程。单线程机制大大降低了Redis内部实现的复杂性。Hash的lazy Rehash、Lpush和其他”线程不安全”的命令可以在没有锁的情况下执行。

Redis 6.0为什么要引入多线程?

Redis把所有的数据都放在内存中,内存的响应时间约为100纳秒。对于小数据包,Redis服务器可以处理8万到10万个QPS。这也是Redis处理的极限。对于80%的公司来说,单台Redis进行处理就足够了。

但随着业务场景越来越复杂,有些公司的交易量动辄上亿,因此需要更大的QPS。常见的解决方案是在分布式架构中对数据进行分区,使用多台服务器,但这种方案有非常大的弊端,比如需要管理的Redis服务器太多,维护成本高;有些适合单台Redis服务器的命令不适用于数据分区;数据分区无法解决热读写问题;数据偏斜、重分布和放大/缩小变得更加复杂等。

从Redis本身的角度来看,由于Redis执行过程中,网络的读写系统调用占据了大部分CPU时间,瓶颈主要是网络的IO消耗。优化的方向主要有两个。

  • 提高网络IO性能,典型的实现方式如使用DPDK替代内核网络栈等。
  • 使用多线程来充分利用多核,典型的实现如Memcached。

这种协议栈优化的方法与Redis关系不大。支持多线程是最有效、最方便的操作方式。所以总结起来,redis支持多线程主要有两个原因。

  • 可以充分利用服务器的CPU资源,目前主线程只能使用一个核心。
  • 多线程任务可以共享Redis同步IO读写负载。

Redis 6.0默认启用多线程吗?

Redis 6.0的多线程默认是禁用的,只使用主线程。要启用它,需要修改redis.conf配置文件:

1
io-threads-do-reads yes

启用Redis 6.0多线程后,如何设置线程数?

启用多线程后,需要设置线程数,否则不会生效。同时修改redis.conf配置文件

1
io-threads 4

关于线程数的设置,官方是有推荐的。4核建议设置为2或3线程,8核建议设置为6线程。线程数必须小于服务器的线程数。同时需要注意的是,线程数并非越多越好。官方认为,超过8个基本没有意义。

Redis 6.0采用多线程后,性能提升效果如何?

Redis作者antirez在RedisConf 2019分享中提到。Redis 6引入的多线程IO功能,性能至少提升了一倍。也有国内大牛在阿里巴巴云esc用不稳定版测试过。在4线程IO中,GET/SET命令的性能比单线程提升了近一倍。

测试环境:

1
2
Redis Server: Alibaba Cloud Ubuntu 18.04, 8 CPU 2.5 GHZ, 8G memory, host model ecs.ic5.2xlarge
Redis Benchmark Client: Alibaba Cloud Ubuntu 18.04, 8 2.5 GHZ CPU, 8G RAM, host model ecs.ic5.2xlarge

测试结果:

  • 这些性能验证测试没有进行严格的延迟控制和不同并发场景的压力测试。数据仅供验证和参考,不能作为在线指标使用。

  • 如果启用了多线程,至少需要一台4核机器,而且Redis实例已经占用了相当多的CPU时间,很耗时。否则,使用多线程是没有意义的。所以,估计80%的公司开发人员都只会看看。

Redis6.0多线程的实现机制是什么?

现将该过程简单介绍如下:

  1. 主线程负责接收连接建立请求,获取套接字,并将其放入全局等待读取处理队列中
  2. 在主线程处理完读取事件后,通过RR(Round Robin)将这些连接分配给这些IO线程
  3. 主线程被阻塞,等待IO线程完成对socket的读取
  4. 主线程以单线程方式执行请求的命令
  5. 主线程被阻塞,等待IO线程将数据写回socket
  6. 解除绑定并清除等待队列

本设计具有以下特点。

  1. IO线程不是同时读套接字就是同时写
  2. IO线程只负责读写socket解析命令,不负责命令处理工作

启用多线程后,会不会存在线程并发安全问题?

从上面的实现机制可以看出,Redis的多线程部分只用于处理网络数据读写和协议分析,命令的执行仍然是单线程顺序执行。所以我们不需要考虑并发和线程安全问题。

给老奴加个鸡腿吧 🍨.