Redis 的 IO 多路复用,学习研究
自己做个记录,也希望能帮助想要了解的人
最终能理解这个,得益于网络上很多前辈的博客和自己粗看过《深入理解计算机系统》
涉及一些计算机基础知识,会先提及,用一种简单的方式让大家有个基本的概念,以帮助理解最后需要讨论的东西
基础知识
操作系统
操作系统本身可以看做一个特殊的软件,只有操作系统能直接接触计算机硬件,其他软件要访问硬件都必须通过操作系统
操作系统对硬件做抽象,封装接口给软件调用
程序员
↓
软件
↓
操作系统(在内存中)
↓
IO设备(网卡、磁盘、键盘、鼠标等)
常见的操作系统有:
Windows、Linux、Mac
内核和用户空间
首先,内核和用户空间都在内存中
内核:
只要计算机处于开启状态,操作系统(记住它是一种特殊的软件)就会把它的程序和数据放入内存中,由于操作系统的重要性,它会独占内存中的一块区域,这块区域就称之为内核
即,内核就是操作系统常驻内存的区域
用户空间:
而用户空间,就是某个进程启动后,被分配到的一块内存区域
注:
具体的涉及到虚拟地址空间的概念,想要详细了解的童鞋可以看下《深入理解计算机系统》第三版的第十章“虚拟存储器”
IO
具体到 IO,操作系统提供了一些 IO 操作,用于操作 IO 设备的数据,然后应用软件对其进行封装
数据流转:
网络IO:网卡 → 内核 → 用户空间(用户内存)
磁盘IO:磁盘 → 内核 → 用户空间(用户内存)
各个操作系统分别提供了哪些 IO 操作?有哪几种 IO 模型?
IO 操作:
select、poll、epoll、kqueue、evport
IO 模型:
以 Linux 为例,它有五种 IO 模型:
阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动IO模型以及异步IO模型
可参考此链接中的内容,讲的很细:
Redis
Redis 是怎么对操作系统提供的 IO 操作进行封装的?
可参考:
为什么 Redis 使用了单线程 IO 多路复用?为什么那么快?
1、cpu 处理相比于 IO 可以忽略
每个客户端建立连接时,都需要服务端为其 创建 socket 套接字,建立连接
然后该客户端的每个请求都要经历以下几步:
(1)等待请求数据数据从客户端发送过来
(2)将请求数据从内核复制到用户进程的缓冲区(buffer)
(3)对请求数据进行处理(对于 redis 而言,一般就是简单的 get/set)
由于操作简单+只涉及内存,所以第(3)步的处理很简单、很快,主要时间耗在(1)步,所以,如果采用普通 BIO 模式,每个请求都要经历这几步,那么处理十万条数据,就要在(1)步花费大量的时间,这样的话,qps 一定很低。所以就采用了更高效的 IO 多路复用模式,即,将(1)步统一交给第三方(也就是操作系统,操作系统提供了 select、poll、epoll、kqueue、iocp等系统调用函数),结合 redis 的单线程,现在整个处理流程是这样的:
一下子来了一堆请求,线程将这些请求都交给操作系统去处理,让操作系统帮忙完成第(1)步,等到这些请求里的一个或多个走完了第(1)步,就将一个集合交给这个线程,并说,我这里收集到了几个走完第一步的请求,你去走(2)、(3)步吧,于是线程拿着这个集合去遍历,等遍历结束之后。又去检查操作系统那儿有没有(这个线程自己维护了一个 while 循环)走完第(1)步的请求,发现又有一些了,拿到后继续遍历进行(2)、(3)步,如此循环往复。
注:有些 IO 模式是将(1)(2)步都交给操作系统处理了,线程本身只需处理第(3)步
2、瓶颈在带宽,而不在 cpu
由于 数据存放在内存中+处理逻辑简单,导致即使是单线程,Redis 可支持的 qps 也相当大,而当 qps 相当大的时候,首先限制性能的是带宽,即不需要把 cpu 的性能挖掘出来,因为在这之前,带宽就不够用了。所以没有必要为了提高 cpu 利用率而使用多线程处理业务逻辑。
参考资料
1、https://mp.weixin.qq.com/s/XzLHy41JrCV_y3BZpeTgwQ
2、https://draveness.me/redis-io-multiplexing/
3、https://mp.weixin.qq.com/s/vrUiKnwjWb3esk8lfiiOfw
4、《深入理解计算机系统》第三版