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

相关推荐
子兮曰1 小时前
Bun vs Node.js 深度对决:跑分快 4 倍,真实业务只剩 3%,2026 年到底该怎么选?
前端·后端·typescript
码事漫谈1 小时前
把 AI 拆掉,你的系统还能跑吗?
前端·后端
BUG指挥官2 小时前
Sa-Token和Spring Security对比
java·后端·spring
众人皆醒我独醉2 小时前
Token:AI 世界的基本粒子——不是"字",是统计上最优的"字符组合"
后端·面试·llm
Java内核笔记3 小时前
Spring Boot 4 拥抱 Jackson 3:包名迁移、配置改名与自动配置源码剖析
java·后端
Hamm3 小时前
前端转Java+AI全栈?那我推荐几个实战开源项目给你
前端·后端·全栈
凤山老林4 小时前
Spring Boot 定时任务进阶:动态 Cron 与集群防重实战
java·spring boot·后端·定时任务·集群定时任务
站大爷IP5 小时前
被 `asyncio.gather` 和 `wait` 坑惨了:异常处理的天壤之别
后端
张龙6875 小时前
Docker 镜像瘦身实战:从 1.2GB 到 128MB,我踩过的 7 个坑和一套可复制的方法论
运维·后端·docker