5 月 2 日Redis 6.0正式发布了。
这个版本提供了诸多新特性及功能改进,比如新网络协议RESP3,新的集群代理,ACL等,本文围绕其中关注度最高的“多线程的引入”来说明。
Redis6.0之前的版本真的是单线程吗?
Redis在处理客户端的请求时,包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。但如果严格来讲从Redis4.0之后并不是单线程,除了主线程外,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大 key 的删除等等。
Redis6.0为什么要使用多线程?
Redis主要受限于内存和网络。使用了单线程后,可维护性高,不同于多线程系统复杂度的增加和各种问题容易出现的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,同时使得 Redis 内部实现的复杂度大大降低。
但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。在Redis6.0以前,可以处理80,000到100,000 QPS,这也是Redis处理的极限了。
常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,但该方案有非常大的缺点。例如:
-
要管理的Redis服务器太多,维护代价大
-
某些适用于单个Redis服务器的命令不适用于数据分区
-
数据分区无法解决热点读/写问题
-
数据偏斜,重新分配和放大/缩小变得更加复杂
突破Redis自身瓶颈
因为读写网络的read/write系统调用占用了Redis执行期间大部分CPU时间,瓶颈主要在于网络的 IO 消耗,而使用多线程能充分利用多核。
redis支持多线程主要就是两个原因:
• 可以充分利用服务器 CPU 资源。
• 多线程任务可以分摊 Redis 同步 IO 读写负荷
Redis6.0的多线程默认是禁用的,只使用主线程。如需开启需要修改redis.conf配置文件:io-threads-do-reads yes
开启多线程后,还需要设置线程数,否则是不生效的。同样修改redis.conf配置文件
关于线程数的设置,官方建议:4核的机器建议设置为2或3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数。
Redis6.0多线程的实现机制?
1、主线程负责接收建立连接请求,获取 socket 放入全局等待读处理队列
2、主线程处理完读事件之后,通过 RR(Round Robin) 将这些连接分配给这些 IO 线程
3、主线程阻塞等待 IO 线程读取 socket 完毕
4、主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行
5、主线程阻塞等待 IO 线程将数据回写 socket 完毕
6、解除绑定,清空等待队列
该设计有如下特点:
1、IO 线程要么同时在读 socket,要么同时在写,不会同时读或写
2、IO 线程只负责读写 socket 解析命令,不负责命令处理
那么开启多线程后,是否会存在线程并发安全问题?
从上面的实现机制可以看出,Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。所以我们不需要去考虑控制 key、lua、事务,LPUSH/LPOP 等等的并发及线程安全问题。