慢命令与为什么应该避免 KEYS *:Redis 性能杀手面面观

慢命令与为什么应该避免 KEYS *:Redis 性能杀手面面观

什么是慢命令

慢命令(Slow Command)是指时间复杂度为 O(N) 或更差、可能阻塞 Redis 主线程并导致服务短暂”卡顿”的操作。由于 Redis 是单线程模型,一个慢命令会阻塞所有后续请求。

最危险的慢命令:KEYS *

KEYS *              -- 匹配所有 key
KEYS pattern:*      -- 匹配指定模式的 key

KEYS * 的危害

时间复杂度:O(N) —— N 是 key 总数

实际影响
– 如果 Redis 中有 1000 万个 key,KEYS * 需要遍历所有 key
– 遍历期间 Redis 主线程被阻塞,无法处理其他任何请求
– 可能导致连接超时、服务雪崩

生产事故案例:某公司在线上执行 KEYS user:*,数据库中有 500 万 key,执行耗时 3.2 秒。这 3.2 秒内所有业务请求排队等待,导致接口响应超时,大量连接堆积,最终触发服务雪崩。

其他常见慢命令

1. 集合操作类

命令 时间复杂度 说明
SMEMBERS key O(N) 获取集合所有成员,N 为集合大小
SINTER key [key…] O(N*M) 多个集合交集
SUNION key [key…] O(N) 多个集合并集,结果集大
ZRANGE key 0 -1 O(log(N)+M) 获取有序集合所有元素
ZREVRANGE key 0 -1 O(log(N)+M) 同上,逆序
HGETALL key O(N) 获取哈希所有字段,N 为字段数

2. 删除操作

DEL key           -- O(1) 删除单个 key
DEL key1 key2...  -- O(N) 删除大量 key
UNLINK key        -- O(1) 异步删除(推荐)

注意:DEL 一个包含大量元素的集合(如 1000 万成员的 Set),也会阻塞 Redis。

3. 排序操作

SORT key [BY pattern] [LIMIT offset count]

时间复杂度通常为 O(N+M*log(M)),N 是集合大小,M 是返回结果数量。

为什么 KEYS * 是”万恶之源”

问题一:复杂度与数据量线性增长

-- 100 万 key: 阻塞约 40ms
-- 500 万 key: 阻塞约 200ms  
-- 1000 万 key: 阻塞约 400ms+

问题二:无法预测执行时间

由于 key 数量和遍历时间是线性关系,随着业务增长,执行时间不断膨胀,但代码中很难感知这一点。

问题三:阻塞期间可能引发连锁反应

Redis 阻塞 → 请求排队 → 连接池耗尽 → 客户端超时 → 重试激增 → 雪崩。

SCAN:KEYS * 的安全替代方案

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
# 安全的遍历方式
def scan_keys(redis, pattern="*", count=100):
    cursor = 0
    keys = []
    while cursor != 0 or not keys:
        cursor, batch = redis.scan(cursor, match=pattern, count=count)
        keys.extend(batch)
    return keys

SCAN 的优点是每次只返回少量数据,不会长时间阻塞主线程

其他替代方案

需要 错误做法 正确做法
查找匹配的 key KEYS pattern SCAN 游标遍历
获取集合所有元素 SMEMBERS bigset SSCAN 游标遍历
获取哈希所有字段 HGETALL bighash HSCAN 游标遍历
获取有序集合元素 ZRANGE bigzset 0 -1 ZSCAN 游标遍历
删除大量 key DEL keys* UNLINK(异步删除)
统计 key 数量 KEYS * | wc -l DBSIZE 命令

如何发现慢命令

使用 slowlog

SLOWLOG GET 10        -- 获取最近 10 条慢查询
SLOWLOG LEN           -- 慢查询日志条数
SLOWLOG RESET         -- 清空慢查询日志

配置慢查询阈值(配置文件或命令):

CONFIG SET slowlog-log-slower-than 10000  -- 记录超过 10 毫秒的命令
CONFIG SET slowlog-max-len 1000           -- 保留最后 1000 条

最佳实践清单

  1. 生产环境严禁在线上使用 KEYS *
  2. 使用 SCAN 系列命令进行遍历
  3. 设置 slowlog 监控并定期审查
  4. 大集合操作考虑拆分为小批次
  5. 删除大 key 使用 UNLINK 而不是 DEL
  6. 合理设计 key 命名,避免需要模糊匹配
  7. 使用 SORT 时注意数据量

面试要点

  • KEYS * 导致阻塞的核心原因是单线程 + O(N) 复杂度
  • 替代方案是 SCAN 系列命令(每次返回有限的元素)
  • slowlog 是发现慢命令的重要工具
  • UNLINK 是 DEL 的安全替代
  • 时间复杂度是 Redis 面试的核心考点
© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容