KEYS 命令会阻塞 Redis,因其 O(N) 全量遍历键空间并独占主线程;生产环境须用 SCAN 替代,但需正确配置游标、COUNT,集群下需分节点执行,并用 UNLINK 分批删除。

KEYS 命令会阻塞 Redis,因为它强制主线程遍历整个键空间,期间无法处理任何其他请求;生产环境必须用 SCAN 替代,但直接照搬示例代码仍可能翻车。
KEYS 为什么一跑就卡住整个实例
Redis 是单线程模型,所有命令串行执行。KEYS 的时间复杂度是 O(N),N 是当前数据库中 key 的总数。它不是“查索引”,而是老老实实遍历哈希表每个桶、每个节点,对每个 key 做字符串匹配。过程中:
- 持有全局锁(实际是事件循环被独占)
- 不返回中间结果,必须等全部匹配完才一次性打包返回
- 返回结果全在内存里拼接,几百万个 key 的列表本身就会吃掉几百 MB 内存
这就像让快递站唯一分拣员停下手头所有包裹,先清点完仓库全部货品再念清单——仓库越大,“断供”越久。
SCAN 不是无脑替换,游标和 COUNT 得配好
SCAN 看似平滑,但默认行为容易误用:
- 游标从 0 开始,但必须用上一次返回的游标值继续,不能每次都重置为 0
- COUNT 不是“每次返回多少个 key”,而是“最多扫描多少个哈希槽位”,实际返回数量可能远少于该值(尤其 key 分布稀疏时)
- 不设 COUNT 时,Redis 默认只扫 10 个槽,导致迭代次数爆炸,网络往返开销反而更大
建议:
- 初期调试用 COUNT 1000,压测时根据响应延迟调到 5000~10000
- 必须检查返回游标是否为 0 才算遍历结束,不能只看 keys 列表是否为空
- 客户端需自己去重,SCAN 可能重复返回同一个 key(rehash 过程中)
集群环境下 SCAN 不能直接套用单机逻辑
Redis Cluster 把 key 分散在多个节点,SCAN 只作用于当前连接的节点。常见错误:
- 用一个客户端连任意节点执行 SCAN,以为能扫全库 → 实际只扫了 1/8 或 1/16 的 key
- 用 redis-cli -c 连集群后直接 SCAN → 命令会被重定向,游标失效,遍历中断
正确做法:
- 先用 CLUSTER SLOTS 获取所有主节点地址和负责的 slot 范围
- 对每个主节点单独建立连接,分别执行 SCAN
- 或使用支持集群模式的客户端(如 redis-py-cluster),确认其 scan_iter 方法是否真正并行遍历所有节点
真正要删 key 时,别在 SCAN 后立刻 DEL
边扫边删看似顺手,但存在两个隐患:
- 如果业务写入频繁,SCAN 过程中新增的 key 可能被漏掉或重复删(取决于游标位置)
- 大量 DEL 命令堆积会拖慢主线程,尤其遇到大 key 时(DEL 是同步删,UNLINK 才异步)
稳妥方案:
- 先用 SCAN 收集待删 key 列表(控制单次收集数量,比如每次最多 500 个)
- 改用 UNLINK 批量删除,避免阻塞
- 若 key 数量极大(>10 万),考虑分批调度,每批间隔 100ms,防止瞬时压力冲垮监控阈值
SCAN 的游标机制和 rehash 行为是隐藏最深的坑——它不保证强一致性,也不承诺一次不漏,但只要接受“最终可覆盖”这个前提,并控制好单次扫描粒度和删除节奏,就能稳住线上服务。别把它当 KEYES 的无缝替代,而要当成一种需要主动适配的渐进式协议。











