面试官: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 多线程化,进一步压榨网络延迟带来的损耗。

相关推荐
codigger15 分钟前
服务器又卡了?一篇讲透 Linux 性能排查(基础四件套 + perf/strace/火焰图)
linux·后端·性能优化
+VX:Fegn089526 分钟前
计算机毕业设计|基于java+ vue共享单车信息系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
苏渡苇31 分钟前
Spring Insight 里如何把 Span 画成瀑布时间线
后端·spring·spring cloud·springboot·监控·apm
Charlie_Byte1 小时前
Spring AI 2 手把手:把 filesystem MCP Server 跑通(SSE / stdio + 真调工具)
后端
程序猿DD1 小时前
OctaFuse Gateway 2.11.0:流式请求优化、用户折扣策略与错误契约升级
前端·后端
Bs_MoneyMagnet1 小时前
基于springboot+vue的非遗物质文化遗产系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
打工仔折腾 AI2 小时前
Prometheus 告警推钉钉:从单群 Webhook 到跨网络 Alertmanager 实战
人工智能·后端·python·性能优化
console.log('npc')2 小时前
06 — Model 层:数据模型与操作
前端·后端·node.js·express
aramae2 小时前
模拟实现memset()(C语言)
c语言·开发语言·后端
2501_933923252 小时前
Spring Bean作用域揭秘:单例、原型、请求与会话的区别
java·后端·spring·java-ee