云霞资讯网

面试官最爱问的坑:Redis 明明单线程,为什么多核能吃满?

大家好,我是 31 岁的小米,一个写代码十几年、踩坑踩成见坑就想讲故事的技术 UP 主。今天这篇文章的灵感来自上周一个



大家好,我是 31 岁的小米,一个写代码十几年、踩坑踩成见坑就想讲故事的技术 UP 主。今天这篇文章的灵感来自上周一个社招 Java 面试候选人。他坐在我对面,手心微微冒汗,却盯着我一脸笃定地回答:“Redis 是单线程的,所以用不上多核 CPU,这就是 Redis 慢的原因。”

我愣了三秒。Redis 慢?Redis 觉得自己受到侮辱。

我当场把水杯放下,对他说:“兄弟,你这是侮辱 Redis 祖坟级别的优化哲学啊。”

于是,我决定把那天和候选人的对话写成故事,也顺便给所有准备面试的同学讲明白:

Redis 为什么是单线程?单线程怎么做到这么快?明明单线程,为什么也能吃满多核 CPU?我们 Java 程序员又该怎么在大厂面试里把这个题答到面试官心窝子里?

来,故事开始。

那天的面试:候选人说 Redis 利用不了多核,我的嘴角狂抖

那天候选人说完那句话后,我问他:“那你知道 Redis 为啥单线程还这么快吗?”

候选人挠挠头,说:“因为它用的是 C 语言?”

我当场心里默默扣分 10 分。于是我把纸笔推给他,让他画画 Redis 的执行模型。

他画了几根横线,说:“单线程啊,能画什么?”

我又默默扣了 10 分。如果你也觉得“Redis 单线程 = 性能差 = 用不上多核”,那我必须给你补这一课。

Redis 为什么单线程还能这么快?因为它把单线程优化到了极致

我跟候选人说:“Redis 单线程不代表 Redis 整个进程只有一个线程。”

候选人:???

我继续解释:“Redis 单线程只是指 网络 IO 和命令执行是单线程的。但 Redis 本身不是只有一个线程,比如持久化线程、集群通信线程、lazy-free 异步删除线程等等。”

但这不是重点。重点是:

Redis 单线程仍然可以打爆多线程数据库,是因为:

纯内存操作,速度极快

IO 多路复用,单线程也能处理海量并发

避免锁竞争,没有上下文切换的开销

命令执行路径短、纯 C 的极致优化

换句话说:

Redis 单线程并不是因为简单,而是因为极致。Redis 不是不想多线程,是觉得多线程会拖慢它。

我问候选人:“你知道上下文切换是什么吗?”

他:知道,会切来切去浪费 CPU。

我点点头:“对,所以 Redis 创始人 antirez 选择单线程,其实是选择性能。”

说到这里,候选人终于有点恍然大悟。

但重点来了:那 Redis 单线程,怎么吃掉多核 CPU?

候选人接着问我:“那 Redis 既然是单线程,那我服务器 16 核岂不是浪费了?”

我笑了。

事实是:大多数互联网公司的 Redis 集群,可以干到全服 CPU 打满(当然是假设你 QPS 很高的情况下)。

怎么做到的?

方法一:一台机器部署多个 Redis 实例,每个实例绑定不同 CPU 核心

我告诉候选人:“大厂都会这样搞。”

举个例子:

服务器:16 核 64G

部署:8 个 Redis 实例

每个实例绑定一个独立 CPU 核心

实例之间用不同端口运行,如:6379、6380、6381……6386

结果是:

单线程实例 × 多核 CPU = 多实例并行

这就像 8 个高速收费站,不会出现所有车都挤一条道。候选人听到这里已经开始疯狂点头。我继续补刀:

为什么企业喜欢这种方式?

提升多核利用率

一个实例爆掉不影响另外的实例,隔离性强

可以根据业务拆分成多个 Redis 实例(订单 Redis、用户缓存 Redis、商品缓存 Redis…)

便于扩容,可灵活迁移

说白了,就是:

Redis 单线程不等于 Redis 只能用一个核,而是你应该多开几个 Redis。

Redis 官方也认可这种方式。

方法二:使用 Redis Cluster,让不同分片分散在多个 Redis 实例上

我跟候选人说:“如果你只开一个 Redis,那么你确实不能用满多核。但是你建一个 Redis Cluster 呢?”

Redis Cluster 有 16384 个 slot,每个 slot 可以分配给不同的 Redis 节点。比如你有:

redis-7001:master(slot:0~5460)

redis-7002:master(slot:5461~10922)

redis-7003:master(slot:10923~16383)

每个 Redis 节点自己单线程,但是三个节点跑在:

CPU 核 1

CPU 核 2

CPU 核 3

这是不是又利用上多核了?甚至你可以继续扩容:

一台机器跑多个 Redis 节点

多台机器分布更合理地使用 CPU

这样整个集群的 CPU 就能全部吃满。

方法三:Redis 7.0 多线程 IO 支持

我问候选人:“你知道 Redis 7.0 开始支持多线程了吗?”

他:啊?不是一直单线程吗?

这是大部分 Java 面试者常犯的错误。Redis 6.0 起加入多线程,但不是执行命令用多线程,而是:多线程处理网络 IO:read/write/writev/sendfile

也就是说:

多线程处理网络读写

单线程执行命令

结果是:

IO 吞吐提升明显

网络瓶颈被解决

Redis 单核性能更上一层楼

这就叫:

“命令执行单线程,网络 IO 多线程”

很多人误解以为 Redis 完整变成多线程了,其实不是。但这个优化让 Redis 可以利用更多 CPU 核心。

方法四:使用 Pipeline + 批量命令降本增效

然后我问候选人:“你知道为什么你 Redis CPU 只占 5% 吗?”

他:因为 Redis 太快?

我说:“不是,是因为你的应用太慢。”大部分 Java 系统访问 Redis 时:

一个请求只查一次 Redis

每次查 Redis 都走一次网络消耗

QPS 低到 Redis 根本不屑用 CPU

如果你用 Pipeline:

一次发 20 条命令

一次返回结果

你的 Redis 负载立刻提高,CPU 使用率上升。Redis 都等着你 Java 应用多来点请求。

方法五:用 Lua 脚本降低命令往返次数

我继续补充:

如果你把 10 个操作组合成 1 个 Lua 脚本执行:

Redis 只处理一次命令

IO 往返都减少

CPU 也能利用得更高

例如做抢购、库存扣减时,Lua 让 Redis 执行路径更短、效率更高。

那天的面试后,候选人突然懂了:Redis 单线程不等于只能用一个核!

我最后问他一个终极问题:

如果你是 Redis 作者,你会把执行命令做好多线程吗?

候选人陷入沉思。几秒后,他说了一句让我很欣慰的话:

“如果 Redis 执行命令多线程,锁竞争、上下文切换、数据一致性都会变复杂,也会变慢……”

我点头。Redis 作者早就说过:

“多线程不是 Redis 的问题解决方案,Redis 的核心是降低延迟。”

如果你真的想利用多核:

开多个 Redis 实例

用 Redis 集群

利用 Redis 6+ 多线程 IO

增加业务 QPS,让 Redis 忙起来

用 Pipeline、Lua 降低网络开销

只要你愿意,Redis 可以把你的 CPU 烧到冒烟。

我给所有准备社招 Java 面试的小伙伴的一些建议

如果你在面试里遇到这个题,千万不要说 Redis 因为单线程所以慢。面试官喜欢听的答案是这样的:

标准满分回答(建议背熟):

Redis 虽然执行命令是单线程的,但并不是不能利用多核 CPU。

企业通常通过以下方式提升多核利用率:

一机多实例:在一台服务器上部署多个 Redis 实例,并绑定到不同 CPU 核心。

Redis Cluster 分片:不同分片运行在不同实例,从而横向扩展 CPU。

Redis 6/7 的多线程 IO 优化:虽然命令执行仍为单线程,但 IO 读写已多线程,可以提升 CPU 利用率。

通过 Pipeline、Lua 脚本减少网络往返,提高吞吐,从而让 Redis 更充分利用 CPU。

因此,Redis 单线程不等于性能差,它通过架构层面的方式实现了对多核 CPU 的高效利用。

总结:单线程 Redis 背后的哲学

我们经常误解“单线程”这个词。单线程不是落后,而是 Redis 对性能的一种极致选择。

Redis 用单线程避免了:

复杂锁

抢占

上下文切换

多线程共享内存问题

它专注做一件事:在内存里以最快速度处理网络请求。

至于多核利用?那是架构层面(多实例、多分片)的事情,不是 Redis 内核的活。

END

希望读完这篇文章,你能在下一次面试里自信地讲:

“Redis 单线程,是为了更快;利用多核,是为了更强。”

如果你喜欢我这种讲故事式的技术文章,点个赞,下次继续给你讲更多面试底层知识,让你面试拿 offer 拿到手软。