Reactor 模式
Redis 是基于 Reactor 模式开发了网络事件处理器,这个处理器称为文件事件处理器。组成结构为4个部分:
- 多套接字
- IO 多路复用程序
- 文件事件派发器
- 事件处理器。
在这里插入图片描述
一般来说 Redis 的瓶颈并不在 CPU,而在内存和网络。如果要使用 CPU 多核,可以搭建多个 Redis 实例来解决。
Redis 6.0 为什么要引入多线程呢?
Redis 的瓶颈并不在 CPU,而在内存和网络。
- 可以充分利用服务器 CPU 资源,目前主线程只能利用一个核。
- 多线程任务可以分摊 Redis 同步 IO 读写负荷。
内存不够的话,可以加内存或者做数据结构优化和其他优化等,但网络的性能优化才是大头,网络 IO 的读写在 Redis 整个执行期间占用了大部分的 CPU 时间,如果把网络处理这部分做成多线程处理方式,那对整个 Redis 的性能会有很大的提升。
Redis 6.0 多线程的优化方向如下:
- 提高网络 IO 性能,典型的实现比如使用 DPDK 来替代内核网络栈的方式。
- 使用多线程充分利用多核,典型的实现比如 Memcached
Redis 6.0 默认是否开启了多线程?
否,在conf文件进行配置
io-threads-do-reads yes
io-threads 线程数
官方建议:4 核的机器建议设置为 2 或 3 个线程,8 核的建议设置为 6 个线程,线程数一定要小于机器核数,尽量不超过8个。
Redis 6.0 多线程实现机制
流程简述如下:
- 主线程负责接收建立连接请求,获取 socket 放入全局等待读处理队列
- 主线程处理完读事件之后,通过 RR(Round Robin) 将这些连接分配给这些 IO 线程
- 主线程阻塞等待 IO 线程读取 socket 完毕
- 主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行
- 主线程阻塞等待 IO 线程将数据回写 socket 完毕
- 解除绑定,清空等待队列
该设计有如下特点:
- IO 线程要么同时在读 Socket,要么同时在写,不会同时读或写。
- IO 线程只负责读写 Socket 解析命令,不负责命令处理。
开启多线程后,是否会存在线程并发安全问题?
不会,Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。
Redis 线程中经常提到 IO 多路复用,如何理解?
这是 IO 模型的一种,即经典的 Reactor 设计模式,有时也称为异步阻塞 IO。
多路指的是多个 Socket 连接,复用指的是复用一个线程。多路复用主要有三种技术:Select,Poll,Epoll。
Epoll 是最新的也是目前最好的多路复用技术。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了 Redis 具有很高的吞吐量。
Redis6.0与Memcached多线程模型对比:
相同点:都采用了 master线程-worker 线程的模型 不同点:Memcached 执行主逻辑也是在 worker 线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解。而 Redis 把处理逻辑交还给 master 线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题。
参考资料
- Rdis作者Antirez的博客:http://antirez.com
- https://www.zhihu.com/question/26943938/answer/68773398
- https://zhuanlan.zhihu.com/p/76788470
- http://www.web-lovers.com/redis-source-6-rc-mult-thread.html
- https://ruby-china.org/topics/38957
- https://redis.io/topics/faq#redis-is-single-threaded-how-can-i-exploit-multiple-cpu--cores
- https://juejin.im/post/5e9ae485f265da47b04d95d2