Fork me on GitHub

redis的单线程和io多路复用

redis是全部只有一个线程吗?

Redis是单线程,主要是指Redis的网络IO和键值读写是一个线程完成的,这也是Redis对外提供键值存储服务的主要流程。但Redis的其他功能,比如持久化、异步删除、集群数据同步等,是由额外的线程执行的(Redis 4.0增加功能)。

所以从整体看,Redis并不只有一个线程完成了所有工作,而是主要的网络IO读写、键值服务都是一个线程完成的。比如bigkey的删除,一定是采用unlink去进行异步的删除,而不是直接del,会阻塞其他客户端的命令执行。

Redis为什么采用单线程?

  • 主要是一个基于数据结构的内存操作服务,如果用多线程需要考虑对数据结构并发操作的线程安全问题,比如对临界资源进行排队,反而增加了复杂度和效率。
  • 对于Redis来说,瓶颈不在CPU,而在内存和网络带宽,采用单线程去处理读写命令即可。设计更简单。

redis单线程为什么这么快?

  1. 基于内存进行操作,效率高于磁盘
  2. 处理命令是单线程,避免频繁的上下文切换和临界资源的锁竞争。
  3. 底层使用优化过的数据结构,比如hash表和跳表等,读取的时间复杂度都很低。
  4. 采用了非阻塞的IO多路复用模型,可以非阻塞的同时处理多个客户端的socket请求。

IO多路复用模型

阻塞IO模型与阻塞点

比如对于一个get请求,服务端处理的步骤如下:

  • 监听客户端请求(bind/listen)
  • 和客户端建立连接(accept)
  • 从socket中读取数据(recv),java中就是IO的read操作。
  • 解析客户端的请求(parse)
  • 执行get操作
  • 返回给客户端结果,向socket中写回数据(send)。

image-20220701065501898

其中阻塞点在于accept建立连接和recv读取数据(联想Java中的BIO),比如服务端如果阻塞在read数据,就无法响应其他客户端的连接请求,是阻塞的。

非阻塞模式

socket网络模型本身支持非阻塞模式。

  • 针对监听套接字,可以设置非阻塞模式:当服务端调用accept()但一直未有连接到达时,Redis线程可以返回处理其他操作,不需要一直阻塞,有一套机制在监听套接字有连接时通知到Redis。
  • 同样的针对已连接套接字,Redis调用recv()之后,没有数据到达进行读操作,也可以先返回非阻塞,利用相同机制当有数据读时通知到Redis

基于多路复用的高性能IO模型

IO多路复用是指的一个线程处理多个IO流,在linux中是通过select/poll/epoll三个机制实现的。

简单来说,IO多路复用就是内核监测文件的读写事件,再通知Redis完成对应的回调操作,保证Redis的非阻塞IO能顺利完成。

多路:多个socket连接
复用:复用一个线程来处理多个socket连接请求事件、

在Redis的单线程模型中,IO多路复用是在内核中,同时存在多个监听套接字和已连接套接字,内核会一直监听这些监听套接字和已连接套接字,如果有对应的请求到达,才会交给Redis的服务端线程处理,Redis服务端的单线程通过文件事件处理队列去排队,文件事件处理器去分派处理,达到多个客户端的socket请求(多路),单线程处理(复用)的高性能线程模型。

Redis IO模型中,利用内核的epoll机制,让内核去监听客户端的socket连接,Redis并不会阻塞在某个特定的accept或者read请求上,当epoll监听到对应的事件请求,会放入就绪队列中,通过基于事件的回调来通知到Redis来处理对应的请求。

image-20220701065522776

Redis使用基于epoll实现的网络通信模型,可以避免Redis自身的线程去轮询FD来监听是否有请求事件发生,而是交给了内核,由epoll来完成监听。Redis基于事件回调自身只从事件队列中拿出事件来处理,及时响应客户端的请求,达到IO多路复用的效果。

Redis的IO多路复用的工作模型:
image-20220701065553339

Redis 6.0 中的多线程模型是怎么样的?

Redis6.0的多线程只是说多线程来处理事件的读写和协议解析,真正执行对命令的还是单线程机制。默认是不开启的,这样的多线程模型为了提高IO效率,解决在网络IO上的瓶颈。

在 Redis 6.0 中新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),将最耗时的Socket的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。

image-20220701065603847

-------------本文结束感谢您的阅读-------------

本文标题:redis的单线程和io多路复用

文章作者:夸克

发布时间:2021年04月01日 - 12:04

最后更新:2022年07月01日 - 06:07

原始链接:https://zhanglijun1217.github.io/2021/04/01/redis的单线程和io多路复用/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。