<p>KEYS * 会拖垮 Redis 单线程,因其阻塞式全量扫描键空间,导致主线程无法处理其他请求,引发连接堆积、超时与雪崩;Redis 6.0+ 默认禁用,推荐用 SCAN 替代。</p>

为什么KEYS * 会拖垮Redis单线程
因为 KEYS 是阻塞式全量扫描,它会遍历整个键空间(底层是 dict 或 zipmap),期间 Redis 主线程无法处理其他任何请求。哪怕数据库只有几十万 key,KEYS * 也可能卡住几百毫秒——在高并发场景下,这直接引发客户端连接堆积、超时、级联雪崩。
更隐蔽的问题是:它不支持模式匹配的增量返回,也没法加超时或分页控制;运维排查时随手一查,就可能让线上服务抖动。
- Redis 6.0+ 默认禁用
KEYS指令(通过rename-command KEYS ""配置) - 某些云厂商 Redis 实例(如阿里云 Tair、腾讯云 CRS)已默认屏蔽
KEYS -
KEYS在集群模式下行为不一致(只查当前节点),容易误判数据分布
SCAN 替代 KEYS 的正确姿势
SCAN 是非阻塞游标迭代器,每次只扫描一小片哈希槽,把压力分散到多次调用中。但它不是“换个命令就行”,必须配合客户端逻辑才能真正替代 KEYS。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用
SCAN代替KEYS pattern:例如SCAN 0 MATCH user:* COUNT 100,其中COUNT是建议值(非精确),实际返回数量可能更少 - 游标必须完整流转:从
0开始,直到返回游标为0才算完成一轮遍历;中间任意一次断开都需保存当前游标重试 -
MATCH支持 glob 风格通配符(*、?、[abc]),不支持正则;若需复杂过滤,得在客户端二次筛选 - 注意重复风险:
SCAN不保证强一致性,期间增删 key 可能导致漏扫或重复,业务上需容忍(比如统计类场景可接受误差)
常见 SCAN 使用错误和绕过方案
很多团队改用 SCAN 后仍出问题,核心是没理解它的语义边界。
- 写死
COUNT 1:看似“更轻”,实则网络往返激增、总耗时反而更长;合理值一般设为100~1000(视 key 平均长度和内存碎片率调整) - 忽略返回游标直接当“翻页”用:比如前端传
page=2就调SCAN 2,这是错的——游标是内部哈希状态,不能手动构造 - 在 Lua 脚本里调
SCAN:Redis 禁止在脚本中使用SCAN(会报ERR This Redis command is not allowed from scripts),只能由客户端驱动 - 需要精确总数?别硬算:用
SCAN+ 客户端去重计数,或改用INFO keyspace查各 db 的 keycount 估算(但不包含过期未清理的 key)
生产环境强制禁用 KEYS 的配置方式
光靠规范约束不可靠,必须从配置层堵死入口。Redis 提供 rename-command 机制,但要注意版本差异和集群兼容性。
- 在
redis.conf中添加:rename-command KEYS ""(清空指令名,调用时直接报ERR unknown command) - 不要写成
rename-command KEYS "my_keys":这等于换了个名字放行,毫无意义 - 集群模式下需对每个节点单独配置,并重启生效;部分托管服务(如 AWS ElastiCache)不开放该配置,需联系支持或改用代理层拦截
- 验证是否生效:
redis-cli KEYS *应返回错误,而非结果;同时检查监控中rejected_commands指标是否上升
SCAN 的游标设计、COUNT 的弹性、以及禁用 KEY 的配置粒度,都是容易被当成“简单替换”而跳过的细节。真正在意稳定性的系统,不会只改一个命令,而是把扫描逻辑下沉到统一 SDK 或中间件里做兜底封装。

















