Redis 是单线程还是多线程?

#中间件#面试八股

Redis 是单线程还是多线程?

结论:命令执行是单线程的,网络 IO 与部分后台任务是多线程的。Redis 6.0 起引入 IO 多线程只分担 socket 读写与协议解析,命令仍在主线程串行执行;4.0 起引入后台线程处理慢删除与 AOF fsync。所以准确说法是"核心命令单线程,周边多线程"。

一、哪些线程在干活

线程/机制引入版本职责是否影响命令原子性
主线程始终解析命令、执行命令、维护数据结构是,唯一执行者
IO 线程(io-threads)6.0读 socket、解析请求、写回响应否,命令仍交主线程
bio close file4.0异步关闭大文件 fd、释放对象否,UNLINK/FLUSHASYNC 后台做
bio AOF fsync4.0everysec 策略下后台刷盘否,最多丢 1 秒
bio lazy free4.0大 key 惰性释放内存否,释放与命令解耦
集群总线线程3.0Cluster gossip 通信否,独立端口

二、6.0 的 IO 多线程到底多在哪

        6.0 之前(单线程全包)              6.0 之后(IO 多线程)
   ┌──────────────────────────┐      ┌──────────────────────────────┐
   │ 主线程:epoll_wait        │      │ 主线程:epoll_wait,分发任务   │
   │        read → 解析 → 执行 │      │   ├─ IO 线程组:read + 解析   │
   │        → 写回             │      │   ├─ 主线程:执行命令(串行)  │
   └──────────────────────────┘      │   └─ IO 线程组:写回响应      │
                                      └──────────────────────────────┘

关键限制:同一时刻只有一个线程在执行命令,因此不需要给字典、跳表加锁,Lua、MULTI、INCR 的原子性依旧成立。多线程只处理"一次请求中与内存数据结构无关"的部分,收益出现在网络带宽打满、单核跑满而 CPU 有富余的场景。

# redis.conf:默认关闭,按机器核数谨慎开启
io-threads 4
io-threads-do-reads yes

经验值:io-threads 不超过 8,且小于等于物理核数;4 核机器开 23 个即可。开太多反而因为线程调度与锁竞争掉性能,官方压测显示 68 线程后收益趋于平坦。

三、验证方式

# 查看 IO 线程配置与运行状态
redis-cli CONFIG GET io-threads
redis-cli INFO server | grep -E "redis_version|io_threads_active"
redis-cli INFO commandstats | head -20

# 观察主线程是否被慢命令拖住(latency 监控)
redis-cli --latency -h 127.0.0.1
redis-cli SLOWLOG GET 10
# 单线程模型下必须避免的阻塞命令
KEYS *                 # O(N),全库扫描
FLUSHALL               # 同步释放,用 FLUSHALL ASYNC
HGETALL big_hash       # 大 key 返回,网络与内存双杀
DEL big_key            # 同步释放,用 UNLINK

四、单机撑不住时的扩展顺序

手段成本收益注意事项
加 Pipeline / 批量命令低RTT 从 N 次降到 1 次单次批量别过大,避免内存峰值
加本地缓存挡住热点中减少 80% 以上读请求需处理失效广播
读写分离(主写从读)中读能力线性扩展从库有延迟,读到旧数据
主从 + 多分片高容量与 QPS 同时扩展跨分片事务与批量操作受限
IO 多线程低网络密集型场景提升 30%~100%核数不足时无效

扩展的优先级是"先省请求、再加副本、最后才分片",因为分片会带来跨槽操作、事务与运维复杂度,代价最高。

五、常见追问

  • 问:单线程怎么处理成千上万个连接?答:epoll 多路复用 + 非阻塞 IO,一个线程轮询就绪事件,不为每个连接创建线程,避免了上下文切换与内存开销。
  • 问:既然单线程,为什么还需要分布式锁?答:单线程保证"单条命令原子",但"读取-判断-写回"跨多条命令仍需 Lua、WATCH 或分布式锁才能原子。
  • 问:6.0 为什么不把命令执行也改成多线程?答:多线程执行要为核心数据结构加锁,锁竞争与复杂度会吃掉收益;真正的扩展手段是分实例、Cluster 分片,而不是单实例多线程。
  • 问:Redis 7.0 有什么线程相关变化?答:引入多线程处理 AOF rewrite 的 IO、以及 Function 与 ACL 的增强,但命令执行模型未变。
  • 问:单线程会不会成为瓶颈?答:会。单实例通常 8~10 万 QPS 量级,热点 key 或大 key 会显著拉低;解决方向是拆分 key、加本地缓存、横向扩分片。

小结

  • 记住一句话:命令单线程、IO 可多线程、后台任务另有线程。
  • 单线程换来无锁与原子性,代价是单核上限,扩展靠多实例与 Cluster。
  • 生产上真正拖慢 Redis 的往往不是"单线程",而是大 key、O(N) 命令和集中过期。
笔记加载中…