Lazy loaded image
Redis丨Redis 为什么这么快?别再只答“内存 + 单线程”了
Words 4594Read Time 12 min
2025-12-8
type
Post
status
Published
date
Dec 8, 2025
slug
redis5
summary
tags
技术探索
category
icon
password
最近整理 Redis 知识点的时候,我又看到了一个特别经典的问题:
Redis 为什么这么快?
以前我看到这个问题,第一反应也是标准八股答案:
因为 Redis 基于内存,读写速度比磁盘快很多;而且 Redis 是单线程模型,可以避免线程切换和锁竞争。
这个回答不能说错,但后来我越学越发现,它其实只答到了第一层。
因为只要继续往下追问几个问题,一般人就回答不出来了:
如果单线程这么快,为什么 Redis 一开始不直接设计成多线程?
一个线程能跑很高的 QPS,那开 4 个线程是不是就能更快?
很多资料会继续解释:
多线程会有上下文切换和锁竞争,反而会拖慢性能。
但这个解释我后来觉得也不够。
因为上下文切换和锁竞争确实有成本,可这个成本真的大到让 Redis 放弃使用多核 CPU 吗?
更关键的是:
如果单线程永远更适合 Redis,那为什么 Redis 6.0 之后又引入了多线程?
这个问题把我问住了。我才意识到,自己以前只是记住了几个关键词:
内存快、单线程、I/O 多路复用。
但并没有真正理解 Redis 的性能模型。
Redis 快,不是因为某一个关键词有魔法,而是因为它在内存、网络、线程模型和命令执行路径之间做了一整套工程取舍。

先纠正一个误区:Redis 不是整个进程只有一个线程

很多人一开口就说:
Redis 是单线程的。
更准确的说法应该是:
Redis 的核心命令执行路径主要由主线程串行完成。
也就是说,客户端发来的 GET、SET、HGET、ZADD 这类命令,对 Redis 核心数据结构的读写,主要是在主线程里完成的。
但这不等于 Redis 整个进程只有一个线程。
Redis 还可能有后台线程、持久化相关任务、异步删除、AOF 重写、Redis 6.0 之后的 I/O 线程等。
所以,Redis 的核心命令执行是主线程串行模型,但整个 Redis 进程并不是只有一个线程。

Redis 快,不只是因为内存快

Redis 基于内存,这是它快的基础。
但如果你只答“内存快”,面试官很容易继续追问:
所有内存数据库都一样快吗?
为什么 Redis 单线程还能处理大量连接?
为什么不是一个连接一个线程?
为什么 Redis 6 又引入多线程?
所以,Redis 快不能只归因于“内存”。
更完整的理解应该是:
Redis 快,是因为它把一次请求的处理链路设计得非常短。
一次普通的 Redis 请求,大致会经历:
客户端发送请求
→ Redis 监听到读事件
→ 读取请求并解析协议
→ 主线程操作内存数据结构
→ 写回响应
这条链路里,Redis 尽量避免了三类成本:
第一,避免频繁访问磁盘。
第二,避免为每个客户端连接创建一个线程。
第三,避免核心数据结构上的复杂锁竞争。
所以 Redis 的高性能,应该理解成一套组合拳:
内存存储 + 高效数据结构 + 非阻塞 I/O + I/O 多路复用 + 主线程串行执行命令 + 简单协议 + 短命令路径。

单线程为什么还能扛住高并发连接?

很多人把“单线程”和“只能处理一个连接”混为一谈。
这是错误的。
Redis 的单线程不是傻傻地阻塞在某一个客户端连接上,而是基于非阻塞 I/O + I/O 多路复用 + 事件循环来处理大量连接。
简单理解就是:
Redis 不会为每个客户端创建一个线程,而是让一个主线程通过操作系统提供的 I/O 多路复用机制,同时监听大量客户端连接。
哪个连接有读事件,Redis 就去读。
哪个连接可以写,Redis 就去写。
没有事件时,Redis 不会傻等某一个客户端。
这就是 I/O 多路复用的价值:
它解决的不是“让命令并行执行”,而是“让一个线程可以高效管理大量连接”。
【配图 1:Redis I/O 多路复用事件循环图】
notion image
I/O 多路复用让 Redis 不需要一个连接一个线程,也能同时管理大量客户端连接。
这就是 Redis 单线程还能支撑高并发的关键。
它不是靠多线程堆资源,而是靠事件驱动模型减少阻塞和线程调度成本。

为什么不直接把命令执行改成多线程?

这个问题才是面试官真正想考的。
假设 Redis 把核心命令执行改成多线程,会发生什么?
比如多个线程同时操作同一个排行榜:
线程 A 执行:ZADD rank user1 100
线程 B 执行:ZINCRBY rank 10 user1
线程 C 执行:ZRANGE rank 0 10
如果这些命令真的并行执行,Redis 就必须考虑很多问题:
第一个问题:底层数据结构怎么加锁?
Redis 的 key 空间、hash、list、set、zset 等结构都可能被并发访问。如果多个线程同时修改同一个结构,就必须引入锁或者更复杂的并发控制。
第二个问题:命令执行顺序怎么保证?
Redis 的很多语义依赖命令顺序。客户端先发 SET,再发 GET,正常情况下应该能读到前面写入的值。如果多线程并行执行,命令顺序就会变得更复杂。
第三个问题:事务、Lua、复制传播怎么处理?
Redis 的事务、Lua 脚本、主从复制、AOF 追加,都依赖相对清晰的命令执行顺序。一旦命令多线程执行,整个系统的复杂度会明显上升。
第四个问题:收益是否值得?
Redis 大多数命令本身非常短,很多命令就是一次内存数据结构访问。
如果为了这些很短的命令引入复杂锁机制,可能锁竞争、同步开销、代码复杂度反而超过收益。
所以 Redis 早期选择主线程串行执行命令,不是因为它不知道多线程,而是因为对 Redis 这种内存型 KV 系统来说:
多线程命令执行带来的复杂度,不一定能换来足够稳定的收益。
这也是 Redis 单线程模型的核心取舍:
把核心数据结构留在主线程串行访问,换来简单、稳定、可预测的执行模型。

那 Redis 6 为什么又引入多线程?

这个问题最容易把人问懵。
因为很多人前面刚说完:
Redis 单线程快,是因为避免多线程开销。
结果面试官马上问:
那 Redis 6 为什么又引入多线程?
这里一定要讲清楚:
Redis 6 引入的是 I/O 多线程,不是把核心命令执行改成多线程。
一个 Redis 请求大致可以拆成四段:
网络读取
→ 协议解析
→ 命令执行
→ 响应写回
Redis 6 之前,这些事情主要由主线程完成。
问题是,随着网络吞吐越来越高、连接数越来越多、value 越来越大,主线程会花越来越多时间在网络读写上。
尤其是响应写回,如果返回的数据比较大,主线程会被 socket 写操作消耗不少时间。
但网络读写这类工作,并不一定必须由主线程亲自完成。
所以 Redis 6 的优化思路是:
核心命令执行仍然由主线程完成,但部分网络 I/O 工作可以交给 I/O 线程处理。
【配图 2:Redis 请求处理链路图】
Redis 6 之前,这条链路的大部分工作主要由主线程承担。当网络读写成本上升时,主线程会被 I/O 消耗拖住。
【配图 3:Redis 6 I/O 多线程模型图】
notion image
Redis 6 的关键不是把命令执行改成多线程,而是把适合并行的网络 I/O 拆出去,让主线程更专注于命令执行。
这就是 Redis 6 多线程设计最精妙的地方。
它既利用了多核 CPU 来分担网络 I/O,又没有破坏核心数据结构的串行执行模型。
所以,Redis 6 的多线程是一次边界非常清晰的增强:
适合串行的命令执行继续串行,适合并行的网络 I/O 拆出去并行。

怎么回答Redis 为什么这么快?

高级回答:
Redis 快不是某一个点的结果,而是一整套性能模型:内存数据结构、短命令路径、事件驱动网络模型、主线程串行命令执行、I/O 多线程演进共同作用。生产环境里还要关注慢命令、big key、hot key、内存碎片、持久化、网络 I/O 和 P99 延迟。

Redis 快,那为什么生产环境还会变慢?

到这里,我们讲的是 Redis 为什么能快。
但生产环境里,真正重要的不只是:
Redis 为什么快?
还要知道:
为什么一个本来很快的 Redis,会突然变慢?
Redis 变慢,很多时候不是因为 QPS 不够,而是因为某些操作把主线程事件循环卡住了。
因为 Redis 的核心命令执行主要是主线程串行完成的。
一个慢操作执行时间过长,后面的请求就只能排队。
所以 Redis 的性能问题,不能只看平均耗时,也不能只看 QPS。
更应该看:
  • P99 延迟
  • P999 延迟
  • 慢查询数量
  • big key 分布
  • hot key 分布
  • 主线程 CPU
  • 网络输入输出
  • 内存碎片率
  • AOF/RDB 持久化影响
瓶颈类型
常见原因
典型现象
排查方向
网络 I/O
响应体过大、连接数过多、RTT 高
QPS 上不去,延迟升高
看网络流量、连接数、pipeline 使用方式
慢命令
KEYS、超大集合操作、复杂 Lua
P99/P999 延迟抖动
看 SLOWLOG、命令耗时统计
big key
单个 value 太大,集合元素太多
阻塞主线程,删除慢,迁移慢
扫描 big key,拆分 key
hot key
单个 key 请求过于集中
单线程 CPU 被打满
热点拆分、本地缓存、读写分离
持久化
AOF fsync、RDB fork、AOF rewrite
周期性延迟毛刺
看 fork 耗时、AOF 策略、磁盘 I/O
内存问题
碎片率高、触发淘汰、写时复制
内存升高,延迟波动
看 INFO memory、碎片率、淘汰指标
客户端问题
短连接过多、连接池不合理、pipeline 使用不当
连接抖动,吞吐不稳
看连接池配置、pipeline 批次、客户端超时
这里要特别强调一点:
Redis 真正可怕的不是普通 GET/SET,而是那些会长时间占用主线程的操作。
比如:
  • KEYS 全量扫描
  • 大 Hash 的 HGETALL
  • 大 Set 的 SMEMBERS
  • 大 ZSet 的范围查询
  • 复杂 Lua 脚本
  • 超大 key 删除
  • 大 value 读写
这些操作一旦执行时间过长,就会让后面的请求全部排队。
所以 Redis 的性能问题,本质上经常不是“不够快”,而是:
某个慢操作破坏了主线程事件循环的节奏。

生产环境 Redis 变慢应该怎么排查?

建议优先看下面几个方向。
第一,看 SLOWLOG。
先确认有没有慢命令。
如果存在 KEYS、大集合操作、复杂 Lua、超大范围查询,就要优先处理。
第二,看命令统计。
通过命令维度判断哪些命令调用最多,哪些命令耗时最高。
很多时候 Redis 变慢,不是所有命令都慢,而是某几个命令把主线程拖住了。
第三,看 big key 和 hot key。
big key 会导致读写、删除、迁移、持久化都变慢。
hot key 会导致请求集中打到某一个 key 上,最终把单线程 CPU 打满。
第四,看内存。
重点看内存碎片率、是否触发淘汰、是否存在异常增长、是否有写时复制带来的内存抖动。
Redis 是内存数据库,它的很多性能问题最后都会反映到内存上。
第五,看持久化。
如果开启了 AOF 或 RDB,要关注 fsync、rewrite、fork 这些操作。
Redis 平时处理请求很快,但持久化涉及磁盘 I/O 和 fork,可能带来周期性延迟毛刺。
第六,看网络。
如果 value 很大、连接数很多、RTT 很高,Redis 服务端本身可能很快,但整体响应仍然会慢。
这时要关注 pipeline、连接池、请求批次、网络输入输出流量。
生产排查 Redis 性能问题时,我觉得更重要的是思维:
是谁占住了主线程?
是命令慢,还是网络慢?
是内存抖动,还是持久化抖动?
是服务端慢,还是客户端使用方式不合理?

面试时应该怎么回答?

你可以这样答:
Redis 快首先是因为它是内存型数据库,大多数命令执行路径很短,底层数据结构也比较高效。
但 Redis 快不只是因为内存。它的核心命令执行主要由主线程串行完成,这样可以避免多线程访问共享数据结构带来的复杂锁竞争,让命令执行顺序更简单、更可预测。
同时,Redis 使用非阻塞 I/O 和 I/O 多路复用,通过事件循环让一个线程管理大量客户端连接。它是哪个连接有事件就处理哪个连接,所以可以用较少的线程资源支撑高并发连接。
到 Redis 6 之后,Redis 又引入了 I/O 多线程。这里的多线程不是把核心命令执行改成多线程,而是把部分网络读写交给 I/O 线程处理,主线程仍然负责核心命令执行。这样既能利用多核优化网络 I/O,又不会破坏 Redis 核心数据结构的串行执行模型。
所以我理解 Redis 的高性能,本质上是内存数据结构、短命令路径、事件驱动网络模型、主线程串行执行和 I/O 多线程演进共同作用的结果。生产环境里还要关注慢命令、big key、hot key、内存碎片、持久化、网络 I/O 和 P99 延迟。
这段回答基本能覆盖面试官想听的几个关键点:
  • Redis 单线程到底单在哪里
  • 为什么单线程还能处理高并发
  • 为什么不直接多线程执行命令
  • Redis 6 多线程到底优化了什么
  • 生产环境 Redis 为什么还可能变慢

总结

Redis 快,不是因为“单线程”这三个字有魔法。
它真正快在一整套性能模型:
  • 内存存储
  • 高效数据结构
  • 非阻塞 I/O
  • I/O 多路复用
  • 主线程串行执行命令
  • 避免核心数据结构加锁
  • Redis 6 I/O 多线程优化网络读写
早期 Redis 选择主线程串行执行命令,是因为大多数命令足够短,瓶颈通常不在命令计算本身。
串行模型反而能避免锁竞争,让系统更简单、更稳定、更可预测。
Redis 6 引入 I/O 多线程,也不是推翻单线程模型,而是把网络读写这类更适合并行的工作拆出去,让主线程更专注于命令执行。
所以,下次面试官再问 Redis 为什么快,不要只说:
因为 Redis 基于内存,而且是单线程。
更好的回答是:
Redis 的高性能来自一整套设计:数据在内存里,命令执行路径短,核心数据结构由主线程串行访问,避免复杂锁竞争;网络层通过非阻塞 I/O 和 I/O 多路复用管理大量连接;Redis 6 又引入 I/O 多线程,缓解高吞吐场景下的网络读写瓶颈。但核心命令执行仍然保持主线程串行,这才是 Redis 线程模型设计的关键。
回到首页