Redis 真的是“单线程”吗?

不完全正确!

我们常说的“Redis 是单线程”,指的是:

Redis 的核心数据操作(如 GET、SET、DEL、INCR 等)

而 Redis 的其他线程包括:

  • 后台线程:用于 持久化(bgsave、bgrewriteaof)
  • 后台线程:用于 异步释放内存(lazyfree)
  • Redis 6.0+:网络 I/O 多线程(但仍由主线程调度)

📌 所以更准确的说法是
Redis 核心逻辑是单线程 + I/O 多路复用 + 事件驱动

核心原理:I/O 多路复用(I/O Multiplexing)

什么是 I/O 多路复用?

在高并发场景下,传统阻塞 I/O 模型需要为每个连接创建一个线程,成本极高。

I/O 多路复用 允许:

一个线程同时监听多个 socket 连接的 I/O 事件(如可读、可写)

当某个 socket 有数据可读时,操作系统通知 Redis 主线程,然后处理该请求。

事件驱动模型:Redis 如何处理请求?

Redis 的事件循环(Event Loop)是其高性能的核心。流程如下:

+------------------+|   客户端连接      |+--------+---------+|v+------------------+|   epoll_wait()   | ← 监听所有 socket+--------+---------+|v+------------------+| 有事件就绪?(是) | → 处理可读/可写事件+--------+---------+|v+------------------+| 执行命令(GET/SET)| ← 单线程执行,原子性+--------+---------+|v+------------------+| 返回响应给客户端  |+------------------+

关键优势:

优势

说明

✅ 无锁竞争

单线程操作数据,无需加锁

✅ 原子性保证

命令执行是原子的

✅ 低上下文切换开销

无需线程调度

✅ 高吞吐

快速响应大量小请求

为什么 KEYS * 会阻塞 Redis?

虽然 Redis 使用 I/O 多路复用处理网络,但命令执行仍在主线程

KEYS *

该命令会:

  1. 遍历整个键空间
  2. 如果有 100 万个 key,可能耗时数百毫秒
  3. 在此期间,主线程被占用,无法处理其他请求

📌 结果
所有其他客户端请求被阻塞,导致 服务雪崩

正确做法:使用 SCAN 命令增量遍历:

SCAN 0 MATCH * COUNT 1000

Redis 6.0 多线程:为什么又引入了多线程?

从 Redis 6.0 开始,引入了 I/O 多线程(默认关闭):

# redis.conf
io-threads 4
io-threads-do-only-io yes

多线程负责什么?

  • ✅ 网络 I/O 读写(接收请求、发送响应)
  • ❌ 不负责命令执行(仍由主线程完成)

为什么引入?

  • 现代服务器 CPU 核心多,单线程无法充分利用
  • 网络 I/O 成为瓶颈(尤其大 value 传输)
  • 多线程处理 I/O,主线程专注命令执行

📌 性能提升:在高并发大 value 场景下,QPS 可提升 2~3 倍。