面试官:Redis 单线程为什么还这么快?这道题答错的人真不少

先说结论。Redis 快,不是因为「单线程」本身快,而是三件事叠在一起:数据在内存、单线程避开了上下文切换和锁竞争、IO 多路复用让一个线程扛住海量连接。

前两条面试官基本不会深问。那问题来了------第三点 IO 多路复用才是真正的考点。下面把它一次讲透。

一、快的三条根因

第一,数据在内存。

这是最主要的原因。

内存读写是纳秒级,磁盘是毫秒级,差了几个数量级。

Redis 所有操作都基于内存,天然就快。

第二,单线程反而省了开销。

多线程听着快,但线程数超过 CPU 核数就要上下文切换,切换本身耗性能。

更麻烦的是线程安全:

多线程共享数据就得上锁,锁竞争又会把并行逼成串行。

Redis 干脆单线程执行命令,没有切换、没有锁,简单直接。

第三,IO 多路复用 + 非阻塞 IO。

这是它能用单线程扛高并发的关键,也是下面要展开的重点。

二、瓶颈在网络,不在执行

要理解第三点,先建立一个判断:

既然命令都是内存操作,执行本身就极快,那 Redis 的性能瓶颈其实不是 CPU,而是网络延迟。

说白了,慢的不是「算」,是「等数据来、等数据走」。

所以优化的方向不是多开线程去算,而是别让线程干等网络。

IO 多路复用解决的就是这件事。

三、IO 效率的两个敌人

讲清多路复用之前,得先知道 IO 慢在哪。

Linux 下,进程分用户空间和内核空间:

用户空间权限低,不能直接操作网卡、磁盘这些硬件,必须借助内核空间的接口。

发一条消息,数据要从用户缓冲区拷到内核缓冲区,再由内核写进网卡;

收一条消息,反过来从网卡读到内核缓冲区,再拷回用户缓冲区。

整个过程里,拖慢 IO 的有两件事:

  • 无效等待:内核还没收到数据,用户进程只能干等。
  • 数据拷贝:用户态和内核态之间来回拷,拷贝期间进程也是阻塞的。

后面几种 IO 模型,都是在和这两件事较劲。

四、三种 IO 模型:阻塞、非阻塞、多路复用

模型 第一阶段(等数据) 第二阶段(拷数据) 问题
阻塞 IO 阻塞,干等 阻塞 两阶段都卡,一个连接卡住全卡
非阻塞 IO 非阻塞,但轮询 阻塞 轮询造成 CPU 空转
IO 多路复用 阻塞在监听,但一次管多个 阻塞 单线程同时管多个连接,效率高

阻塞 IO 最直白:

等数据就绪要等,数据从内核拷到用户也要等,两个阶段都阻塞。

更糟的是它一次只能盯一个连接,这个连接在等数据,后面排队的连接全得跟着等。

非阻塞 IO 把第一阶段变成「不停轮询」:

内核没数据就立刻返回,进程反复问「好了没」。

表面不阻塞了,但本质是盲等,CPU 被空转耗光,性能也没起来。

IO 多路复用换了个思路:

一个线程同时监听一堆 Socket,谁就绪通知谁,不用傻等,也不用盲问。

五、select/poll 与 epoll 的差别

多路复用在 Linux 上有三种实现:select、poll、epoll。前两个是一路人,epoll 是升级版。

打个比方。

餐厅里很多桌客人要点餐:

  • select / poll 像每张桌上装了个按钮,全连到服务员头顶的一盏灯。任意一位按了,灯就亮,但服务员不知道是谁按的,只能一桌一桌去问「你要点餐吗」,找到为止。连接越多,遍历越慢。
  • epoll 像按钮直接连到服务员面前的屏幕,谁按了,屏幕上直接显示「3 号桌就绪」。服务员不用遍历,直接去处理。

差别就在这:

select/poll 只告诉你「有 Socket 就绪」,但不说是哪个,你只能逐个翻;

epoll 在通知的同时把就绪的 Socket 直接交给你,省掉了遍历。

这也是 Redis 在高并发下依然稳的关键。

六、Redis 的网络模型:多路复用 + 事件派发

Redis 的网络模型,就是 IO 多路复用,再叠一套自己的事件派发机制。

多路复用负责监听客户端连接(每一个 Socket 连接)。

连接上之后会有不同事件:

有读请求,有写请求。

多路复用把就绪的连接和事件捞出来,派发给对应的事件处理器:

  • 连接应答处理器:处理客户端的连接应答。
  • 命令请求处理器:接收客户端参数、转成 Redis 指令、执行、产出结果。
  • 命令回复处理器:把结果响应回客户端。

一句话:

请求进来交给多路复用做事件监听,提前定义好各种处理器,什么事件就派发给什么处理器。

七、Redis 6.0 之后为什么又有多线程?

注意,上面说的都是单线程模型。

Redis 6.0 引入了多线程,但只加在两处网络 IO 上:

  • 接收网络请求、解析命令时,多线程并行把客户端发来的字节流解析成 Redis 命令。
  • 响应结果输出时,多线程并行把结果写回网络。

命令的执行仍然是主线程串行跑,线程安全、基于内存、不影响性能。

真正拖慢的,是「收字节」和「发字节」这两段网络 IO,所以多线程只加在这里。

到这里就能回答面试官了:

Redis 用单线程执行命令,靠 IO 多路复用 + 事件派发扛住高并发;6.0 后又把网络收发的 IO 多线程化,进一步压榨网络延迟带来的损耗。

相关推荐
2601_962055977 小时前
跟据spring boot版本,查看对应的tomcat,并查看可支持的tomcat的版本范围
spring boot·后端·tomcat
Lost of 程序猿8 小时前
ASP.NET Core API 幂等性设计深度实战:从一次重复申领事故说起
后端·asp.net
QQ_21696290969 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端
郑州光合科技余经理11 小时前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php
IT_陈寒11 小时前
Vue的嵌套组件竟然吃掉了我的事件?
前端·人工智能·后端
大辉狼_音频架构12 小时前
进阶:从源码编译 SOF 固件与 topology
后端
大辉狼_音频架构12 小时前
上板:让 SOF 在 FRDM-i.MX8MP 上跑起来
后端
大辉狼_音频架构12 小时前
FRDM-IMX8MP UUU 烧录 eMMC 指南
后端
运行时异常13 小时前
【WMS 仓储系统集成 AI Agent 实战】第 3 讲:Spring Security 6 + JWT——addFilterBefore 一词之差,全站 401
java·后端
LinMINGJing00713 小时前
PageHeaderData:Page 的页面头
后端