Redis 命令时间复杂度为什么重要?哪些命令容易阻塞?
简化版
Redis 命令复杂度重要,是因为核心命令执行会占用主线程。GET、SET 这类 O(1) 命令通常很快,而 KEYS、大集合 SMEMBERS、大列表范围读取、复杂 Lua、大 key 删除等 O(N) 操作可能阻塞实例。
详细版
Redis 快不代表所有命令都快。命令是否危险,要看复杂度、数据规模、返回结果大小和执行频率。
例如 HGET user:1 name 是 O(1),但 HGETALL user:1 如果 hash 里有 10 万个字段,就是 O(N)。同样,LRANGE list 0 -1 在小列表上没问题,在百万元素列表上就可能造成延迟尖刺。
线上治理要做到:上线前评审复杂度,禁止危险命令,控制单 key 大小,使用分页/分批,配合 slowlog、latency monitor 和命令统计定位问题。
完整版教学
一、为什么 Redis 快也怕慢命令
Redis 的高性能来自内存访问、高效数据结构、事件驱动和较少线程切换。但当某条命令执行时间长,它仍会占住命令执行路径。
如果一条命令耗时 200ms,在这 200ms 内后续请求排队,表现出来就是大量普通 GET 也变慢。用户看到的是整体 Redis 抖动,而不是某个慢命令单独慢。
时间线:
0ms GET a
1ms SMEMBERS huge:set 执行 200ms
2ms GET b 等到 201ms 后才执行
二、复杂度要结合数据规模看
命令文档里的 O(N) 不是抽象数学,它直接和 key 内元素数量绑定。SMEMBERS 对 100 个元素没压力,对 100 万个元素就完全不同。
一个常见误判是只看命令名,不看参数范围。LRANGE list 0 99 取 100 条可以接受,LRANGE list 0 -1 取全量就危险。
| 命令模式 | 风险原因 | 替代思路 |
|---|---|---|
KEYS * | 全库扫描 | SCAN 分批 |
HGETALL 大 hash | 全字段返回 | HSCAN 或字段拆分 |
SMEMBERS 大 set | 全集合返回 | SSCAN 或分页索引 |
LRANGE 0 -1 | 全列表读取 | 限定范围 |
大 key DEL | 同步释放内存 | UNLINK |
三、返回结果大小也是复杂度
有些命令执行计算不复杂,但返回数据巨大,同样会造成网络和客户端压力。Redis 服务端把响应写到输出缓冲,客户端读得慢还会撑大内存。
例如一次返回 50MB 数据,即使命令本身很快,网络传输也要时间。这个连接上的响应未发送完,还会影响客户端和服务端资源。
所以评审 Redis 命令时要问两个问题:服务端要做多少工作?会返回多少数据?
四、Lua 脚本和事务也可能阻塞
Lua 脚本在 Redis 内原子执行,执行期间其他命令不能插队。短小 Lua 很适合做原子判断和更新,但复杂循环、访问大量 key、调用大范围命令会阻塞。
-- 危险示意:对大量 key 做循环处理
for i, key in ipairs(KEYS) do
redis.call("HGETALL", key)
end
事务 MULTI/EXEC 里的命令也不是“免费批处理”。如果里面包含多个重命令,执行时仍会集中占用 Redis。
五、如何监控和治理慢命令
Redis 提供 SLOWLOG 记录执行超过阈值的命令。它记录的是命令执行时间,不包含网络传输时间,所以还要结合客户端 RT、Redis CPU 和带宽看。
LATENCY DOCTOR、命令统计、实例 p99、bigkey 扫描都能帮助定位。治理动作包括禁用危险命令、改写分页、拆 key、限流后台任务、使用只读副本做离线扫描。
一个实用策略是:线上代码评审要求说明 Redis 命令复杂度,尤其是集合、列表、hash、zset 的全量读取。
六、常见误区与追问
- 误区:Redis 单条命令都是毫秒级。 O(N) 命令、大返回和复杂 Lua 都可能让延迟到百毫秒甚至秒级。
- 误区:慢查询只看数据库,Redis 不用管。 Redis slowlog 同样是线上稳定性关键指标。
- 误区:
HGETALL是 hash 的常规读取方式。 小 hash 可以,大 hash 要按字段或扫描读取。 - 追问:
DEL为什么也可能慢? 删除大对象会释放大量内存,同步释放可能阻塞主线程。 - 追问:如何在上线前预防? 做命令复杂度评审、限制 key 大小、封装 Redis SDK 禁用危险命令。
七、加强记忆
记忆钩子:Redis 快的是“短命令、小数据、少返回”;一旦让它搬全库、搬大集合、跑长脚本,它也会在主线程上喘不过气。
回答这题时别只列危险命令,要把复杂度、数据规模、返回体积、监控工具和治理策略连起来。这样能体现你理解 Redis 延迟抖动的根因。