适用读者:听说"Redis 单线程"但没想通"为什么快"的人。本系列第一篇。
前置:S05-B05(线程池)、S02-B09(IO 多路复用)。
实测环境:Redis 7.0.15(临时实例 :16379),2026-11-05。
覆盖旧文:864(65) 865(72) 877(79) 1839(65) 1837(98) 1901(87) 500(37) 1900(19) = 522 点。
覆盖扩展:E-S12-001(事件循环与多路复用)、E-S12-002(6.0 的 IO 多线程)。
下一篇:S12-B02 · 五种数据结构与底层编码。
1. 一句话结论
Redis 快不是因为"单线程",而是因为"纯内存 + 高效数据结构 + IO 多路复用 + 避免锁竞争"。单线程是结果,不是原因——它的代价是"一个慢命令会卡住所有人"。
2. 实测:慢命令如何卡住整个实例
用 Lua 忙等 2 秒(模拟一个耗时命令),期间从另一个客户端发 PING:
redis-cli -p 16379 eval "local t=redis.call('time')[1]; while tonumber(redis.call('time')[1])-t < 2 do end return 'done'" 0 &
redis-cli -p 16379 ping
实测输出:
阻塞期间一次 PING 耗时: 1441 ms ← 排队等了 1.4 秒
Lua 返回: done
空闲时一次 PING 耗时: 16 ms ← 正常
这就是"单线程"最直接的证据:命令串行执行,一个慢命令让后续所有请求排队。
| 命令 | 复杂度 | 生产可用性 |
|---|---|---|
KEYS * |
O(N) | ❌ 禁用(J05 实测 20 万 key 耗时 0.28 s) |
HGETALL 大 hash |
O(N) | ⚠️ 大 key 时危险 |
SMEMBERS 大 set |
O(N) | ⚠️ 同上 |
| Lua 里的循环 | 不可控 | ❌ 必须限制 |
3. 快的真正原因(E-S12-001)
| 原因 | 说明 |
|---|---|
| 纯内存 | 无磁盘 IO(持久化是异步的) |
| 高效数据结构 | SDS/quicklist/listpack/skiplist,按规模切换编码(B02) |
| IO 多路复用 | 单线程处理大量连接(epoll) |
| 避免锁与上下文切换 | 没有多线程竞争开销 |
| 协议简单 | RESP 文本协议,解析快 |
判据:"瓶颈是 CPU 还是 IO?" Redis 的瓶颈几乎总是网络带宽或单核 CPU,不是锁。
4. 6.0 之后还是"单线程"吗(E-S12-002)
| 部分 | 线程模型 |
|---|---|
| 命令执行 | 单线程(仍然) |
| 网络 IO(读写 socket) | 可多线程(io-threads) |
| 持久化(AOF fsync/RDB) | 子进程/后台线程 |
惰性删除(UNLINK/lazyfree) |
后台线程 |
实测:
redis_version:7.0.15
io_threads_active:0 ← 默认未启用 IO 线程
结论:"命令执行仍是单线程"——所以"慢命令卡全局"的风险没有变。
5. SDS:为什么不直接用 C 字符串(A-877)
| C 字符串 | SDS |
|---|---|
需要 strlen 遍历(O(n)) |
记录长度(O(1)) |
二进制不安全(遇 \0 截断) |
二进制安全 |
| 扩容需手动管理 | 预分配 + 惰性释放 |
"Redis 为什么能存二进制" 的答案就在这里。
6. 五个误区
| 误区 | 真相 |
|---|---|
| "单线程所以快" | 快是内存+数据结构+多路复用;单线程是取舍 |
| "Redis 完全单线程" | 6.0 起 IO 可多线程,命令执行仍单线程 |
| "多核没用" | 可以起多实例/Cluster 吃多核 |
"KEYS * 只是慢一点" |
阻塞全局(实测 0.28 s) |
| "Lua 一定安全" | 循环写错会阻塞整个实例 |
7. 自检
- 实测里阻塞期间
PING用了多久?说明了什么? - Redis 快的五个真实原因?
- 6.0 之后哪些部分不再是单线程?
- SDS 相比 C 字符串的三个优势?
- 为什么生产禁用
KEYS?
8. 下一篇
S12-B02 · 五种数据结构与底层编码
单线程讲完,下一篇讲"为什么同一类型会换编码"——实测 embstr/quicklist/listpack/hashtable 的切换。
本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-864/865/877/1839/1837/1901/500/1900;扩展点 E-S12-001/002。阻塞与版本信息为 Redis 7.0.15 实测。