目录
91.Redis 6.0之前为什么一直不用多线程?
官方曾做过类似问题的回复:使用Redis时,几乎不存在CPU成为瓶颈的情况,Redis主要受限于内存和网络。例如一个普通的Linux系统上,Redis通过使用pipelining每秒可以处理100万个请求,所以如果应用程序主要使用o(N)或者 o(log(N))的命令,它几乎不会占用太多CPU。
使用了单线程后,可维护性高。多线程模型虽然在某些方面表现优异,但是它却引入程序执行顺序的不确定性,带来了并发读写的一些列问题,增加了系统复杂度,同时可能存在线程切换,甚至加锁解锁,死锁造成的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,因此没有必要使用多线程。单线程机制使得Redis内部实现复杂度大大降低,Hash的惰性Rehash,Lpush等等“线程不安全”的命令都可以无锁进行。
92.Redis 6.0为什么要引入多线程呢?
Redis将所有的数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis服务器可以处理80000到100000QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。
但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,但该方案有非常大的缺点,例如要管理的Redis服务器太多,维护代价大;某些适用于单个Redis服务器的命令不适用于数据分区;数据分区无法解释热点读/写问题;数据偏斜,重新分配和放大/缩小变得更加复杂等。
从Redis自身角度来说,因为读写网络的read/write系统调用占用了Redis执行期间大部分CPU时间,瓶颈主要在于网络的IO消耗,优化主要有两个方面:
提高网络IO性能,典型的实现比如使用DPDK来代替内核网络栈的方式
使用多线程充分利用多核,典型的实现比如Memcached
协议栈优化的这种方式跟Redis关系不大,支持多线程是一种最有效最便捷的操作方式。所以总结起来,Redis支持多线程主要就是两个原因:
可以充分利用服务器CPU资源,目前主线程只能利用一个核
多线程任务可以分摊Redis同步IO读写负荷
93.Redis 6.0默认是否开启了多线程?
Redis 6.0的多线程默认是禁用的,只使用主线程。如需要开启需要修改redis.conf配置文件:io-threads-do-redis yes
94.Redis 6.0多线程开启时,线程数如何设置?
开启多线程后,还需要设置线程数,否则是不生效的。同样修改redis.conf配置文件io-threads 4
关于线程数的设置,官方有一个建议:4核的机器建议设置为2或者3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数。还要注意的是,线程数并不是越大越好,官方认为超过了8个基本就没有什么意义了。
95.Redis 6.0多线程的实现机制?
核心思路是,将主线程的IO读写任务拆分出来给一组独立线程执行,使得多个socket的读写可以并优化。
主线程负责接收建立连接的请求,获取socket放到全局等待处理队列。
主线程处理完读事件之后,通过Round Robin将这些连接分配给IO线程(并不会等待队列满)
主线程阻塞等待IO线程读取socket完毕
主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但不执行
主线程阻塞等待IO线程将数据回写socket完毕
解除绑定,清空等待队列
该线程有如下特点:
IO线程要么同时在读socket,要么同时在写,不会同时读或者写
IO线程只负责读写socket解析命令,不负责命令处理(主线程串行执行命令)
96.开启多线程后,是否会存在线程并发安全问题?
Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行,因此不存在线程的并发安全问题。