必须用scan替代keys,但scan无法按类型过滤,需配合type等命令二次判断,并处理游标、重复、中断续扫等问题。

直接说结论:用 SCAN 替代 KEYS 是必须的,但仅靠 SCAN 本身无法按数据类型过滤;必须配合 TYPE 或 PTTL 等命令二次判断,且需处理游标、重复、中断续扫等现实问题。
为什么不能直接用 SCAN + MATCH 筛选数据类型?
SCAN 的 MATCH 参数只支持 glob 模式匹配 key 名(如 "user:*"),不感知 value 类型。Redis 没有内置类似 SCAN TYPE string 的语法。
- 常见误操作:
SCAN 0 MATCH "*" TYPE hash—— 这会报错ERR syntax error,因为TYPE不是SCAN的合法参数 - 真实限制:服务端不提供类型索引,所有类型判断必须在客户端发起额外请求
- 后果:一次
SCAN返回 100 个 key,你得发 100 次TYPE请求——网络开销和延迟陡增
安全遍历 string 类型 key 的最小可行方案
核心思路:分批 SCAN → 并行查 TYPE → 过滤出 "string" → 合并结果。注意控制并发和游标状态。
- 用
COUNT显式指定批次大小(如SCAN 0 COUNT 50),避免默认 10 导致轮询次数过多 - 每次收到新游标(
cursor)后必须保存,下次从该值继续;游标为"0"表示遍历结束 - 对每个返回的 key,用 pipeline 批量发
TYPE(例如 redis-py 的pipeline().type(...).execute()),减少 RTT - 若某次
SCAN返回空数组但游标非"0",说明可能被其他客户端删除了部分 key,继续用当前游标重试
避免阻塞和漏扫的关键细节
SCAN 不阻塞,但不等于“绝对安全”——实际使用中容易踩三个坑:
- 游标超时失效:某些 Redis 代理(如 Twemproxy)或云服务(如阿里云 Tair)会丢弃长时间未推进的游标,建议单次扫描耗时控制在 5 秒内
- key 在扫描中被删:
SCAN可能跳过刚被DEL的 key,也可能重复返回刚被SET的 key,这是设计使然,无法避免 - 没有原子性保证:你拿到一批 key 后再查
TYPE,期间 key 的 type 可能已被改写(比如INCR把 string 变成 number),所以结果只能视为“快照” - 生产环境务必加超时和重试:比如设置
SCAN单次响应 >2s 就中断,记录当前游标,后续从该点恢复
真正省事但有代价的替代方案
如果业务允许少量延迟或额外存储,可以绕过实时遍历:
- 写时记录:所有写入
string类型 key 的地方,同步SADD string_keys {key},读时用SMEMBERS string_keys+SCAN分页取 - 用 RedisJSON 或 ReJSON 模块:它把结构化数据存为 JSON,可用
JSON.TYPE查询字段类型,但仍是额外请求,且不解决基础类型遍历问题 - 监控辅助:开启
CONFIG SET notify-keyspace-events KEA,监听set事件写入白名单,但无法回溯历史 key
最麻烦的地方不在命令怎么写,而在于你要自己维护游标状态、处理网络失败、接受结果非强一致——这些逻辑一旦漏掉一环,线上扫出来的 key 列表就不可信。











