redis的线程模型

众所周知,redis是单线程的。但是官方提供的压测数据,redis每秒可以支持10万的QPS,这个数据很惊人。但是redis的单线程不是说redis内部就只有一条线程。而是说redis处理请求的线程是单线程。像刷盘这种操作,有另外的线程去做的。
redis能支持如此之高的并发量,其原因有二:
1 .纯内存操作
2 .IO多路复用
1 .多路复用
关于IO多路复用,涉及到两个词。多路和复用
多路:多路socket连接
复用:多个socket连接使用的是一个线程
redis的多路复用依赖的是事件轮询机制。
事件轮询机制有几个API,select、epoll、kqueue、evport。
在linux系统上,redis采用的是epoll函数。
在macos、FreeBSD系统上采用的是kqueue函数
在solaries系统上,redis采用的是evport函数
如果以上函数,操作系统均不支持,此时采用select函数。select在所有操作系统上都会存在。
以上描述,使得redis做到了跨平台性
此处,我们只介绍一下select函数,其实本质上和其他几个API差不多
当客户端socket发起读写请求时,redis服务端程序调用系统内核的select函数,传入当前socket的文件读写描述符以及一个超时时间timeout。当文件描述符处于可读/可写的状态时。select函数会将该事件立即返回。另外,当timeout到达后,select函数也会立即返回。客户端的整个请求链路,只有在调用select函数时阻塞,其他时候不阻塞
select函数伪代码如下:
read_events,write_events = select (read_fds,write_fds,timeout)
for event in read_events:
handle_read(event.fd)
for event in write_events:
handle_write(event.fd)
handle_others( ) #处理其他事情,如定时任务等
由上可以看出,select返回的是一系列的事件,然后循环处理。此时就达到了复用线程的目的。
拿到事件后,redis的那条单线程就会开始处理这个事件,处理完后。继续过来轮询,于是线程进入了一个死循环,我们把这个死循环称为时间循环。一个循环为一个周期
2 .事件分派器
IO多路复用模块会将已经处于读写就绪状态的事件压入队列。然后事件分派器从队列中挨个取出事件,根据事件的不同类型分配不同的事件处理器(连接应答处理器,命令请求处理器,命令回复处理器)。事件分派器自己不干活,交给别人干活
这个事件分派器的工作模式有一个专有的名词,叫做Reactor模式。和tomcat的处理连接的过程是非常相似的,tomcat内部也采用了Reactor模式
3 .redis工作流程图
在这里插入图片描述
原图出处:https://zhuanlan.zhihu.com/p/65013389
4 .完整的redis请求 我们把以上的知识点,串起来说一下总的流程 客户端socket发起对redis服务端的连接请求,将自己的文件描述符提交给redis的多路复用模块。多路复用模块监听该描述符的状态,一旦该描述符就绪,就会返回描述符对应的事件。然后多路复用模块将事件压入一个队列中。事件分派器从队列中取出事件交给事件处理器进行处理,然后将执行结果返回给socket客户端。 以上,是自己的一点琢磨。应该会有描述欠妥的地方,希望路过的大神能给予批评指正,谢谢!