阅读了 redis 的源码,我终于明白为什么说 redis 是单线程!
开篇
本文主要来探讨一下 redis 的单线程模型,文章前半部分会先引用某网络课程讲解的内容(图片+语言描述),后半部分是本人粗略阅读 redis 源码后整理出来的一份伪代码,用来验证文中前半部分的内容。
本文对标的 redis 版本是 5.x。
正文
redis 涉及的知识点有很多,展开来讲能聊到操作系统,因此为了方便理解,文中做了很多抽象描述。
文件事件处理器
redis 内部使用了一个叫 文件事件处理器( file event handler)的东西,这个文件事件处理器是 单线程 的,所以才有了 redis 是单线程的这一说法。
文件事件处理器的结构如下图:
它包含 4 个部分:
- 多个 socket
- IO 多路复用程序
- 文件事件分派器
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
文件事件处理器 采用 IO 多路复用机制 同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序 会监听多个 socket,会将 socket 产生的事件放入 队列 中排队,事件分派器 每次从队列中取出一个事件,把该事件交给对应的 事件处理器 进行处理。
来看 redis 客户端与服务端的一次通信过程:
1. 接收连接请求:
客户端 socket01 向 redis 的 server socket 请求建立连接,此时 server socket 会产生一个 AE_READABLE 事件,
IO 多路复用程序监听到 server socket 产生的事件后,将该事件压入队列中。
文件事件分派器从队列中获取该事件,交给连接应答处理器。
连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 AE_READABLE 事件与 命令请求处理器 关联。
2. 读取请求内容:
假设此时客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 事件
IO 多路复用程序将事件压入队列
事件分派器从队列中获取到该事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。
命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置。操作完成后,它会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联。
3. 回复请求:
如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件,同样压入队列中,
事件分派器找到相关联的命令回复处理器
由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。
这样便完成了一次通信。
redis 事件处理伪代码
// 入口函数 void aeMain(EventLoop eventLoop) { while(true) { // 文件事件处理器 aeProcessEvents(eventLoop); } } void aeProcessEvents() { // 调用 epoll_wait 函数,等待I/O事件 (IO 多路复用程序) int numevents = aeApiPoll(timeval); for(int i=0; i< numevents; i++) { // 从队列中取出对应的事件 fileEvent = getFromEventQueue(i); // 处理文件事件 (文件事件分派器) processFileEvent(fileEvent); // 处理时间事件。(忽略) processTimeEvent(); } } void processFileEvent() { if event == '读事件' { // 读处理器 processReadFile(); } if event == '写事件' { // 写处理器 processWriteFile(); } }
redis 源码
篇幅原因这里就不贴 redis 的源代码,可以用 vscode 等工具打开 redis 安装目录下的 src 目录,通过全局搜索 aeMain 找到入口。
总结
起初当看到别人说 redis 是单线程时,很容易想成 redis 服务端只开启了一个线程用于做所有事情,然而实际上我们所说的 redis 单线程 只是针对 redis 网络请求模块,即文中提到的文件事件处理器。
另外,在第一次看到文中第一张图时,就感觉像是一个线程(IO多路复用程序)负责往队列写数据,另一个线程(文件事件分派器)负责从队列里面读数据,那么 redis 的单线程到底体现在哪里呢?
最后通过一顿操作,翻到了相关源码,粗略一读才明白所谓的事件分派器、队列等等是这样的。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
全面解析JVM,超详细!
点击蓝色“程序员的时光”关注我,标注“星标”,及时阅读最新技术文章 写在前面: 小伙伴儿们,大家好!今天来学习Java虚拟机相关内容,作为面试必问的知识点,来深入了解一波! 思维导图: JVM思维导图 1,JVM是什么? 1.1,概述 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范。引入Java虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。 所谓java能实现跨平台,是由在不同平台上运行不同的虚拟机决定的,因此java文件的执行不直接在操作系统上执行,而是通过jvm虚拟机执行,我们可以从这张图看到,JVM并没有直接与硬件打交道,而是与操作系统交互用以执行java程序。 跨平台 1.2,JVM运行流程 JVM运行流程 这个是JVM的组成图,由四个部分组成: 类加载器 类加载器的作用是加载类文件到...
- 下一篇
「干货总结」程序员必知必会的十大排序算法
点击上方蓝字关注我设为星标 绪论 身为程序员,十大排序是是所有合格程序员所必备和掌握的,并且热门的算法比如快排、归并排序还可能问的比较细致,对算法性能和复杂度的掌握有要求。bigsai作为一个负责任的Java和数据结构与算法方向的小博主,在这方面肯定不能让读者们有所漏洞。跟着本篇走,带你捋一捋常见的十大排序算法,轻轻松松掌握! 首先对于排序来说大多数人对排序的概念停留在冒泡排序或者JDK中的Arrays.sort(),手写各种排序对很多人来说都是一种奢望,更别说十大排序算法了,不过还好你遇到了本篇文章! 对于排序的分类,主要不同的维度比如复杂度来分、内外部、比较非比较等维度来分类。我们正常讲的十大排序算法是内部排序,我们更多将他们分为两大类:基于「比较和非比较」这个维度去分排序种类。 「非比较类的有桶排序、基数排序、计数排序」。也有很多人将排序归纳为8大排序,那就是因为基数排序、计数排序是建立在桶排序之上或者是一种特殊的桶排序,但是基数排序和计数排序有它特有的特征,所以在这里就将他们归纳为10种经典排序算法。而比较类排序也可分为 比较类排序也有更细致的分法,有基于交换的、基于插入的、基...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS关闭SELinux安全模块
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker快速安装Oracle11G,搭建oracle11g学习环境