面试官:Redis 6.0 之后为什么引入了多线程?

面试回答

Redis 6.0 之后引入多线程,主要是为了解决高并发场景下网络 I/O 成为瓶颈的问题,而不是把命令执行改成多线程。

经典 Redis 模型里,客户端连接读写、协议解析、命令执行、响应返回大多由主线程处理。随着机器网络性能提升、客户端连接数增加、请求和响应数据变大,主线程可能把大量时间花在读 socket、写 socket 和处理协议数据上。这个时候,Redis 的瓶颈不一定是内存数据结构操作,而可能是网络 I/O 和单核 CPU 消耗。

所以 Redis 6.0 的多线程选择了一个比较克制的方案:用 I/O 线程分担网络读写和部分协议处理压力,但命令执行仍然由主线程串行完成。这样既能利用多核提升吞吐,又尽量保留 Redis 原来的单线程执行语义,避免给核心数据结构引入大量锁竞争。

面试中可以总结为:Redis 引入多线程不是因为单线程模型失败了,而是因为在更高并发和更高网络吞吐下,主线程的网络 I/O 成本变得明显。Redis 6.0 把容易并行的 I/O 部分拆出去,把需要保证顺序和原子性的命令执行留在主线程,这是性能和复杂度之间的权衡。

系统讲解

为什么原来的模型会遇到瓶颈

Redis 单线程高性能的原因 中,关键点是 Redis 的核心路径足够短:内存访问、事件驱动、高效数据结构和单线程避免锁竞争。

但这个模型也有边界。一个 Redis 主线程不仅要执行命令,还要处理客户端连接上的数据读写。如果连接数很多、请求体较大、响应结果较大,主线程会在网络 I/O 上消耗大量 CPU 时间。例如大批量 pipeline 请求、返回较大的 MGET 结果、热点实例承载大量短连接或高频请求时,网络读写和协议处理就可能抢占命令执行时间。

这类瓶颈有一个明显特征:单个 Redis 实例的一个 CPU 核心很忙,但机器上其他核心没有被充分利用。继续保持所有 I/O 工作都在主线程中完成,就很难吃满现代多核机器的能力。

多线程具体做了什么

Redis 6.0 的多线程主要是 I/O 多线程。可以把处理流程简化成下面这样:

主线程接收事件

I/O 线程并行读取请求数据

主线程串行执行命令

I/O 线程并行写回响应数据

这里最重要的是中间那一步:命令执行仍然由主线程完成。也就是说,对 Redis 数据结构的读写、命令顺序、单条命令的原子性,仍然遵循原来的主线程串行模型。

为什么这样设计?因为 Redis 的核心数据结构高度共享,例如全局键空间字典、过期字典、跳表、哈希表等。如果让多个线程同时执行命令,就必须引入锁、原子操作或更复杂的并发控制。锁会增加实现复杂度,也可能让性能收益被锁竞争抵消,甚至破坏 Redis 一直强调的低延迟和语义清晰。

为什么不直接多线程执行命令

多线程执行命令看起来可以更充分利用多核,但它会让 Redis 面临三个问题。

首先是并发控制。多个线程同时访问同一个 key、同一个哈希表或同一个跳表时,需要细粒度锁或无锁结构,否则会破坏数据一致性。锁粒度太粗,吞吐提升有限;锁粒度太细,代码复杂度和维护成本都会上升。

其次是命令原子性。Redis 很多命令天然具备“单线程串行执行”的原子语义,客户端也依赖这种行为。如果命令执行并行化,就要重新定义不同命令之间的可见性、顺序和冲突处理。

最后是延迟稳定性。Redis 追求的是低延迟,而不是单纯追求 CPU 并行度。引入复杂锁竞争后,平均吞吐可能提升,但尾延迟可能变差,这对缓存、计数、限流这类在线请求链路并不友好。

因此 Redis 6.0 选择只把相对容易并行、对数据一致性影响较小的网络 I/O 拆出去。

配置示例

Redis I/O 多线程需要通过配置开启,线程数通常设置为小于机器 CPU 核心数的值。示例配置如下:

# 开启 4 个 I/O 线程,包含主线程在内的线程调度由 Redis 内部处理
io-threads 4
 
# 是否让读请求也使用 I/O 线程;不同版本和部署策略下可以按压测结果决定
io-threads-do-reads yes

实际生产环境中,不应该只因为“多线程更快”就直接打开配置。更稳妥的做法是先确认瓶颈确实在网络 I/O 或协议处理上,再通过压测比较开启前后的吞吐、平均延迟和尾延迟。如果瓶颈来自慢命令、大 key、Lua 脚本、持久化压力或网络带宽,多线程 I/O 不一定能解决问题。

解决了什么问题

Redis 6.0 多线程主要改善的是高并发连接下的网络处理能力。它适合缓解这些场景:

  • 请求量很高,主线程大量时间消耗在 socket 读写上。
  • pipeline 较多,请求解析和响应写回压力较大。
  • 响应内容较大,写回客户端的成本明显。
  • 单实例 CPU 一个核心接近打满,但机器还有空闲核心。

但它不能解决所有性能问题。慢命令仍然会阻塞主线程,大 key 的删除和遍历仍然可能造成延迟尖刺,单个实例的命令执行仍然主要受一个主线程约束。如果业务需要更高的总体吞吐,仍然需要考虑分片、Redis Cluster、多实例部署、避免慢命令和控制数据结构规模。

常见追问

追问:Redis 6.0 之后是不是就不是单线程了?

不能这样说。Redis 进程中确实可以有多个线程,但核心命令执行仍然主要由主线程串行完成。更准确的说法是:Redis 6.0 引入了 I/O 多线程,但不是多线程执行命令。

追问:开启 I/O 多线程后,命令还具有原子性吗?

单条命令的原子性仍然成立,因为命令执行阶段仍然在主线程中串行完成。I/O 线程主要处理请求读取和响应写回,不直接并发修改 Redis 的核心数据结构。

追问:什么情况下开启多线程收益不明显?

如果瓶颈是慢命令、复杂 Lua 脚本、大 key 操作、内存容量、内存带宽、持久化抖动或网络带宽上限,那么 I/O 多线程收益可能有限。它优化的是网络 I/O 和协议处理成本,不是把所有命令都并行化。

追问:面试时如何一句话区分 Redis 4.0 和 Redis 6.0 的多线程?

Redis 4.0 开始有后台线程处理惰性释放等任务,目的是减少主线程阻塞;Redis 6.0 引入 I/O 多线程,目的是提升高并发下的网络读写能力。两者都不是把核心命令执行改成多线程。

参考资料

官方资料

Redis 6.0 release notes (Redis, 访问于 2026-04-28)

这份发布说明可以了解 Redis 6.0 引入 I/O threading 的背景,以及它和 Redis 6.0 其他能力之间的关系。

redis.conf (Redis, 访问于 2026-04-28)

配置文件中包含 io-threadsio-threads-do-reads 的说明,适合确认 I/O 多线程的开启方式和使用注意事项。

源码资料

networking.c (Redis, 访问于 2026-04-28)

networking.c 是理解 Redis 客户端连接、请求读取、响应写回和 I/O 线程协作的核心源码入口。