线程模型
Redis 的线程模型是面试高频题:命令执行单线程,网络 IO 多路复用,6.0 起 IO 可多线程。三句话讲清楚,再展开为什么。
事件循环
IO 多路复用 + 单线程事件循环
- IO 多路复用:一个线程用 epoll 同时监听成千上万个 socket,只处理就绪的事件
- 文件事件处理器:连接应答、命令请求、命令回复三类事件,全部串行处理
- 因为命令执行是纯内存操作(微秒级),单线程足够快,还免去了锁竞争
为什么单线程
| 收益 | 说明 |
|---|---|
| 无锁 | 数据结构和命令都不用加锁,没有死锁 |
| 无上下文切换 | 不频繁切换线程 |
| 简单可靠 | 不存在并发 bug,调试容易 |
瓶颈分析:单机 Redis 的瓶颈在网络和内存,不在 CPU。CPU 通常个位数占用,所以多线程收益有限。
6.0 多线程 IO
6.0 引入多线程,但只处理网络读写:
io-threads配置(默认 1,需显式调大并开启io-threads-do-reads才生效,建议大于等于 4 核才开)- IO 线程负责 socket 读写和协议解析,命令执行仍然是单线程
- 收益场景:大流量下网络读写开销明显时(尤其大批量 get/set)
- 不开启的原因:命令执行仍是瓶颈时多线程无济于事
一句话:6.0 的多线程是“IO 多线程”,不是“执行多线程”,执行线程始终唯一。
单线程的代价
- 慢命令阻塞一切:KEYS、大集合的 SMEMBERS、大 key 的删除(DEL)都会卡住整个实例
- 解决办法:KEYS 换 SCAN、删除用 UNLINK(异步)、大 key 拆分(见大 key 篇)
- 长 Lua 脚本同样阻塞
面试追问
- Redis 为什么单线程还快? 内存操作微秒级,单线程免锁免切换;瓶颈在网络和内存不在 CPU
- 单线程怎么处理并发? IO 多路复用,事件驱动,只处理就绪的连接
- 6.0 多线程改了什么? 网络 IO 读写多线程,命令执行仍单线程。io-threads 需显式开
- 哪些命令会阻塞? KEYS、SMEMBERS、DEL 大 key、长 Lua。用 SCAN、UNLINK、拆分替代
- 为什么不用多线程执行命令? 内存操作单线程已足够快,多线程引入锁竞争和复杂度,收益为负