Redis 线程模型

作者: MentallyL | 来源:发表于2017-12-02 23:17 被阅读278次

    https://draveness.me/redis-io-multiplexing

    redis是一个单线程的程序,在IO方面由于是单线程,所以为了提高IO的效率,使用了多路复用的IO模型。

    Blocking I/O

    最原始的Blocking IO为阻塞的IO。读写操作等待用户输入或输出都是阻塞的,每次的IO都是要等待的

    当使用 read 或者 write 对某一个文件描述符(File Descriptor 以下简称 FD),如果当前 FD 不可读或不可写,整个 Redis 服务就不会对其它的操作作出响应,导致整个服务不可用。

    模型图如下(这种图的风格看着好舒服啊):


    image.png

    这种模型在每次有一个FD来的时候会阻塞住,这时候FD的数据在系统中还没有准备好,当准备好了的时候在从内核准备好的FD中把数据复制到进程的内存里,这时候阻塞才会释放。 每次凡是有一个FD那么就会阻塞住其他的。其实一次IO操作可以分成两个操作部分,一个是等待数据准备好,然后在从内核复制,这第二步其实是真正的IO复制操作。有时可能由于一次网络连接,连接准备好了,但是由于网络延迟没有数据传输过来,导致这个整个过程特别的耗时。那么我们怎么改进呢?

    其实操作系统已经考虑了这种情况,每个操作系统都为我们提供了可以检测有哪些FD已经准备好了,所以我们不用在阻塞整个IO操作了,我们可以一次连接多个FD,然后遍历这些FD,有哪些已经准备好了,我们只需要使用这些准备好的进行IO操作即可。

    图片如下:


    image.png image.png

    具体有操作系统有哪些函数可以供我们使用呢?


    image.png

    Redis 会优先选择时间复杂度为 $O(1)$ 的 I/O 多路复用函数作为底层实现,包括 Solaries 10 中的

    evport、Linux 中的

    epoll

    和 macOS/FreeBSD 中的

    kqueue,上述的这些函数都使用了内核内部的结构,并且能够服务几十万的文件描述符。

    但是如果当前编译环境没有上述函数,就会选择

    select

    作为备选方案,由于其在使用时会扫描全部监听的描述符,所以其时间复杂度较差 $O(n)$,并且只能同时服务 1024 个文件描述符,所以一般并不会以

    select

    作为第一方案使用。

    总结

    Redis 对于 I/O 多路复用模块的设计非常简洁,通过宏保证了 I/O 多路复用模块在不同平台上都有着优异的性能,将不同的 I/O 多路复用函数封装成相同的 API 提供给上层使用。
    整个模块使 Redis 能以单进程运行的同时服务成千上万个文件描述符,避免了由于多进程应用的引入导致代码实现复杂度的提升,减少了出错的可能性。

    相关文章

      网友评论

      • zhglance:这种图片基本看不懂单词是什么意思
        luban_:非常同意了

      本文标题:Redis 线程模型

      本文链接:https://www.haomeiwen.com/subject/qyuobxtx.html