Redis 的执行#
网络 I/O 部分#
在 Redis 6.0+ 中,这部分工作由多个 I/O 线程并行处理,主要负责以下任务:
- 网络连接与数据读取: 监听和接收来自客户端的 Socket 网络连接,并将 TCP 缓冲区中的字节流读取到用户态的 Buffer 中。
- 协议解析 (RESP 解析): 将读取到的无意义字节流,按照 Redis 的通信协议(RESP - Redis Serialization Protocol)解析成 Redis 可以理解的命令和参数(例如将字节流转换成
SETkeyvalue的结构)。 - 数据回写 (Socket Write): 当 CPU 完成命令执行后,I/O 线程会将内存中的执行结果(例如
+OK\r\n)序列化,并并发地写入回对应的 Socket 缓冲区,发送给客户端。
网络数据的读写、系统调用(read/write)以及协议的装箱拆箱是纯粹的 I/O 密集型操作。引入多线程处理这部分,可以充分利用多核 CPU 的优势,大幅度提升 Redis 的吞吐量(QPS)。
CPU 执行核心部分#
无论网络 I/O 怎么并发,Redis 真正执行具体命令的阶段,始终是绝对单线程的。 这部分主要负责:
- 命令路由与执行: 从解析好的命令队列中取出命令,去哈希字典(Dict)中寻址,并执行具体的数据结构操作(如操作 String、Zset 的跳表、Hash、List 等)。
- 内存管理: 处理内存的分配与回收。
- 过期与淘汰策略: 定期或惰性地清理带有 TTL 的过期 Key,或者在内存触及
maxmemory阈值时,执行 LRU/LFU 淘汰算法。 - 高级特性处理: 维护和执行 Lua 脚本、事务(MULTI/EXEC)、Pub/Sub 消息分发等机制。
核心特点(为什么坚持单线程执行):
- 无锁化: 因为执行是单线程的,操作底层的 Dict 和各种复杂数据结构时,不需要加任何互斥锁(Mutex)和并发控制,避免了锁竞争带来的开销。
- 原子性: 单个命令(或 Lua 脚本)的执行天然具备原子性。
- 避免上下文切换: 避免了多线程抢占 CPU 资源时的内核上下文切换开销。内存操作的速度处于纳秒级,单线程足以跑满单核的极限。
Redis 是单线程还是多线程#
核心处理逻辑,Redis一直都是单线程的,其它辅助模块也会有一些多线程、多进程的功能,比如:
复制模块用的多进程;
某些异步流程从4.0开始用的多线程,例如 UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC 等非阻塞的删除操作。
网络I/O解包从6.0开始用的是多线程;
但是这种分支模块,都只是辅助,最核心的还是处理架构,这块Redis始终是单线程的。
回答#
Redis 核心处理是单线程的,在6.0中使用了多线程进行I/O解包、回包。其他一些边缘点的,比较使用多线程进行删除数据等异步任务,这个倒是4.0就引入了。
Redis 为什么选择单线程做核心处理#
为什么用单线程,潜台词其实对应的是为什么不用多线程。
很多同学只说多线程有多大成本、多复杂,这样就容易被追问多线程这么多问题,那为什么MySQL等很多组件都是用多线程呢,总有优势吧?为啥Redis不利用这种优势?
所以这里需要从投入产出比分析,我们先看产出:
首先Redis的定位,是内存 k-v 存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不应该成为瓶颈,而瓶颈通常在网络I/O,处理逻辑多线程并不会有太大收益。
再看投入:
引入多线程带来极大的复杂度,比如原来的顺序执行特性就不复存在,为了支持事务的原子性、隔离性,Redis就不得不引入一些很复杂的实现,多线程模式也使得程序调试更加复杂和麻烦,会带来额外的开发成本及运营成本,也更容易犯错。同时,也带来上下文切换成本、同步机制的开销、线程本身也占据内存大小等投入成本。
回答#
我们从投入产出来看。首先如果引入多线程,主要是希望充分利用多核的性能,但Redis的定位,是内存k-v存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不应该成为瓶颈,而瓶颈通常在网络I/O,处理逻辑多线程并不会有太大收益。同时,支持多线程的话,我们需要付出更大的复杂度、以及多线程上下文切换、同步机制的开销等成本。这样综合来看,成本高且收益不大,所以最终选择了不做,事实也证明,单线程的Redis也确实足够高效
Redis 单线程性能如何?#
这个主要考察直观的认知,Redis实际表现为单机(普通8核16G的机器)读10多万,写几万,非常炸裂。可以说自己也用过 redis-benchmark 来测试过。
命令:
redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 10000 -q
结果:
SET: 108695.65 requests per second
GET: 149253.73 requests per second回答#
Redis 单线程的性能是很好的,在普通机器每秒能 10 多万的读性能、几万的写性能。
为什么单线程还能这么快#
这里很多同学都能答到内存存储、高效的数据结构,但是遗漏 I/O 多路复用,其实某种意义上 I/O 复用是非常核心的,因为Redis使用单线程的核心,是瓶颈在I/O而不是CPU,那 I/O 复用正好能对症下药。
- 基于内存操作:Redis的绝大部分操作在内存里就可以实现,数据也存在内存中,与传统的磁盘文件操作相比减少了IO,提高了操作的速度。
- 高效的数据结构:Redis有专门设计了STRING、LIST、HASH等高效的数据结构,依赖各种数据结构提升了读写的效率。
- 采用单线程:单线程操作省去了上下文切换带来的开销和CPU的消耗,同时不存在资源竞争,避免了死锁现象的发生。
- I/O多路复用:采用 I/O 多路复用机制同时监听多个 Socket,根据 Socket 上的事件来选择对应的事件处理器进行处理。
回答#
我认为有三点,一个是内存存储,这是Redis的定位也是快的前提,一个是高效的数据结构,Redis的数据结构可以说是追求极致,持续调优,最后一个是I/O多路复用,Redis的瓶颈在I/O而不是CPU,那I/O复用正好能对症下药。
Redis6.0之后引入了多线程,你知道为什么吗?#
要回答这个问题,就要回归到Redis瓶颈所在:是 I/O 而不是CPU,但是随着互联网发展,请求量巨大的时候单线程在进行同步读写 I/O 的时间,单核 CPU 有时候也是不够用了。
回答#
Redis主要瓶颈是 I/O 而不是CPU,但随着互联网的高速发展,在部分高并发场景,单核 CPU 也不见得处理得过来了,所以针对核心处理流程中的解包、发包这两个CPU耗时操作,进行了多线程优化,充分发挥多核优势
Redis6.0的多线程是默认开启的吗?#
回答#
默认是关闭的,如果想要开启需要用户在 redis.conf 配置文件中修改。默认关闭第一是为了兼容以前的,毕竟很多用户的认知中,Redis是单线程的,第二可能也是认为多线程并不是必要的,在大多数场景不开启也是完全够用的。
Redis 6.0 的多线程主要负责命令执行的哪一块#
Redis主要瓶颈是I/O而不是CPU,但随着互联网的高速发展,在部分高并发场景,单核CPU也不见得处理得过来了,所以针对核心处理流程中的解包、发包这两个CPU耗时操作,进行了多线程优化,充分发挥多核优势
回答#
原来核心流程中的 I/O 处理,包括解包和回包,也就是读写客户端 socket 的 I/O ,这两部分都消耗CPU时间,多线程的引入主要也是为了解决单核 CPU 在大数据下还是不够用的问题。

