大家好,我是程序媛雪儿。众所周知,redis是单线程的,但为什么redis还是那么快?
原因:
1、redis是纯内存操作的
2、采用单线程可以避免不必要的上下文切换
3、使用I/O多路复用模型,非阻塞IO
前两个原因很好理解,那么大家能知道为什么使用非阻塞IO就能提升redis响应速度吗?
这是因为redis的性能瓶颈是网络延迟,而I/O多路复用模型主要就是实现了高效的网络请求。
那今天雪儿就和大家好好聊一聊redis使用的I/O多路复用模型吧。
一、前置概念和工作流程
用户空间和内核空间
用户空间:用户空间必须通过内核提供的接口访问系统资源,不能直接调用
内核空间:可以调用一切资源
Linux系统为了提高IO效率,会在用户和内核空间都加入缓冲区
写数据,要把用户的缓冲数据拷贝到内核缓冲区再写入设备
读数据,要从设备拷贝到内核缓冲区,再拷贝到用户缓冲区
二、常见的IO模型
2.1 阻塞IO
阶段1:用户尝试读取数据,但是数据未到达内核,用户进程要等待数据,处于阻塞状态
阶段2:数据到达内核,从内核拷贝到用户缓冲区,这个拷贝过程,用户进程也处于阻塞状态
2.2非阻塞IO
阶段一:用户尝试读取数据,如果没有数据返回错误信息,用户再尝试读取数据,直到数据就绪,进程不阻塞
阶段二:等内核数据拷贝到用户缓冲区,解除阻塞,处理数据
评价:性能提高并不多,因为虽然阶段一是非阻塞,但是频繁的询问数据是否准备就绪会导致CPU空转,使得CPU使用率暴增
2.3 IO多路复用
利用单个线程来同时监听多个Socket,如果某个Socket可读、可写,就会立刻调用recvfrom进行拷贝数据,处理数据
阶段一:用户调用select,内核监听指定的Socket集合,任意一个或多个socket数据准备就绪就返回readable,这个过程进程是阻塞的
阶段二:用户找到数据准备就绪的socket,依次调用recvfrom读数据拷贝到用户缓冲区,处理数据
评价:因为只有数据准备就绪的用户进程才会调用recvfrom,不会出现没有数据等待数据就绪空等,其他用户进程跟着空等的情况,提高了CPU资源的利用效率。
监听Socket的方式
select、poll、epoll
select和poll只会通知用户进程有socket就绪,但是不确定是哪个socket,需要用户遍历socket确认
epoll则会通知用户进程哪些socket数据就绪
三、redis网络模型
redis网络模型主要是通过IO多路复用来提高网络性能的,并且支持多种事件派发的方法,比如,连接应答处理器(tcpAccepthandler)、命令回复处理器(sendReplyToClient)、命令请求处理器(readQueryFromClient)
连接应答处理器(tcpAccepthandler): 接受新的客户端连接。
命令回复处理器(sendReplyToClient): 将回复缓冲区中的数据发送到客户端。
命令请求处理器(readQueryFromClient):干的事情比较多,会先接收请求数据,把数据转为redis命令,选择并执行命令把结果写入缓冲队列中。
为了提高redis的性能,redis6.0支持多线程,主要是在命令回复处理器(sendReplyToClient)和命令请求处理器(readQueryFromClient)中提供了多线程处理
欢迎大家关注我的微信公众号,程序媛雪儿,雪儿会定期在上面发布编程的知识碎片,也有雪儿博客地址,上面有详细系统的笔记,雪儿是全栈,但是公众号目前主要还是发后端的技术,以后可能也会涉及到一些前端的知识,我们下期见,拜拜~