从处理请求的核心流程谈一谈Redis到底是单线程还是多线程
作者:互联网
从处理请求的核心流程谈一谈Redis到底是单线程还是多线程
随着Redis版本的不断更新,Redis在处理请求方面也在不断的优化,由单线程的概念逐渐引入了多线程的概念。那么Redis到底是单线程还是多线程呢?
在Redis 4.0版本之前,Redis完全是单线程,没有引入多线程这个概念。因为Redis是完全是基于内存操作的,通常情况下CPU不会是redis的瓶颈,于是就采用单线程模型处理请求,如果使用多线程的话,反而会变得更复杂,同时还涉及到了多线程的上下文切换,带来了额外的性能消耗。所以,Redis读写速度快,除了基于内存操作、优化了底层数据结构以外,单线程处理请求也是速度快的原因之一!
到了Redis 4.0版本时,才引入了多线程的概念,但是核心流程还是单线程的,这里核心流程指的是Redis正常处理客户端发送过来的请求,即接收命令 ==> 解析命令 ==> 执行命令 ==> 返回结果。而所谓的多线程并没有涉及到核心流程的处理,只是用来做一些后台处理,比如删除对象等。
在Redis 6.0的时候,又一次加强了多线程的概念,这个版本多线程会涉及到了核心流程。Redis 6.0中多线程主要用于网络的I/O阶段,也就是接收命令和写回结果阶段,而执行命令阶段还是单线程串行执行。
所以,说Redis是单线的这个说法从执行命令阶段来看,是没毛病的!
之所以Redis单线程也很快,是因为:
- 完全是基于内存操作的
- 单线程避免了多线程上下文切换带来的额外消耗
- Redis对底层的数据结构做了优化
- I/O多路复用
接下来我们重点来讲讲Redis的I/O多路复用技术
我们要知道Redis服务器是一个事件驱动程序,是需要靠事件触发服务器对请求的处理操作。服务器需要处理的事件分为了两类:
-
文件事件
Redis服务器是通过套接字与客户端或者其它Redis服务器进行连接的,而文件事件实际上就是指的Redis服务器与客户端或者其它服务器之间的互相通信的一种抽象概述。服务器和客户端之间通信会产生相应的文件事件,服务器通过监听这些产生的文件事件来完成一系列网络通信操作。你可以理解为客户端发送请求给服务器,然后服务器监听到请求之后,处理请求并返回。
-
时间事件
顾名思义,Redis服务器的一些操作需要在特定的时间点才会触发执行,而时间事件就是对这种定时操作的一种抽象概述。
平时我们操作最多的就是文件事件,接下来我们主要来说说文件事件
**Redis基于Reactor模式(事件驱动模型)开发了自己的文件事件处理器。**由于文件事件处理器是单线程执行的,也就是我们之前说的执行命令阶段,所以说Redis是单线程模型是没问题的。
那么既然是单线程模型,那Redis是如何监听大量的客户端连接的呢?
Redis使用I/O多路复用技术实现了监听客户端大量连接,或者说监听大量的套接字。当被监听的套接字准备好执行连接应答、读取、写入、关闭等操作时,与操作相对应的文件事件也就产生了,Redis服务器会根据套接字目前阶段执行的操作来为套接字关联不同的文件事件处理器,来处理这些事件。虽然文件事件处理器以单线程方式运行,但通过使用IO多路复用技术来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好的与redis服务器中其它同样以单线程方式运行的模块进行对接。
而这个文件事件处理器由4个部分组成:
- 套接字(客户端与服务器之间的连接)
- I/O多路复用程序(监听大量客户端连接的关键技术)
- 文件事件分派器(socket映射到对应的文件处理器)
- 事件处理器(连接、请求、回复、关闭等处理器)
其中I/O多路复用程序负责监听多个套接字,并向文件事件分派器传送产生了事件的套接字。尽管多个文件事件可能并发的发生,但是I/O多路复用程序会将所有产生事件的套接字都放到一个队列里面,然后通过这个队列,有序、同步、每次一个的将套接字发送给文件事件分派器。当一个套接字被对应所关联的事件处理器执行完成之后,I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。
以上就是Redis单线程且高效的原因!
我们说在Redis 4.0版本的时候才引入了多线程的概念,但是多线程不涉及核心流程,直到Redis 6.0版本再次了多线程的概念,涉及到了核心流程。Redis 6.0版本加入多线程 I/O 之后,处理命令的核心流程如下:
-
连接应答
当服务器监听到套接字连接时,主线程将该客户端连接放到全局等待读队列
-
读取数据:
1)主线程将等待读队列的客户端连接通过轮询调度算法分配给 I/O 线程处理;
2)同时主线程也会自己负责处理一个客户端连接的读事件;
3)当主线程处理完该连接的读事件后,会自旋等待所有 I/O 线程处理完毕(这里就有点像Java中基于AQS框架实现的并发工具类CountDownLatch)
-
命令执行:
主线程按照事件被加入全局等待读队列的顺序(保证了执行顺序是正确的),串行执行客户端命令,然后将客户端连接放到全局等待写队列
-
写回结果:
跟等待读队列处理类似,主线程将等待写队列的客户端连接使用轮询调度算法分配给 I/O 线程处理,同时自己也会处理一个,当主线程处理完毕后,会自旋等待所有 I/O 线程处理完毕,最后清空队列。
我们可以看出来,Redis 6.0版本的多线程虽然涉及到了核心流程,但是命令执行阶段还是单线程串行执行的,只是在读取数据和写回结果这两个阶段采用了多线程I/O处理。说白了就是维护了两个队列,一个全局等待读队列,一个全局等待写队列,使得单线程转专注于命令的执行,由多线程处理读写操作,从而提高了I/O读写性能。
以上就是为什么说Redis又是多线程的原因!
标签:单线程,Redis,文件事件,接字,多线程,客户端 来源: https://blog.csdn.net/qq_39794062/article/details/120399138