生产环境必须用 scan 替代 keys,核心是游标分批、不阻塞主线程;需正确设计遍历逻辑,处理重复、漏扫、中断续传和批量操作四类问题,游标驱动循环不可错,count 为扫描粒度提示而非精确条数,须客户端去重并按需补偿漏扫。

生产环境必须用 SCAN 替代 KEYS,核心是靠游标分批、不阻塞主线程。Java 客户端(如 Jedis、Lettuce)调用 SCAN 时,关键不在“怎么写”,而在“怎么设计遍历逻辑”——要处理重复、漏扫、中断续传和批量操作这四类问题。
游标驱动的循环结构不能错
SCAN 不是一次性命令,而是状态延续过程。起始游标为 0,每次返回新游标,直到游标变回 0 才算结束。中间任何一次返回的 key 列表为空,只要游标非 0,就必须继续调用。
- 不要用 for 循环固定次数,要用 do-while 或 while (cursor != 0)
- 每次调用 SCAN 后,必须用返回的新游标作为下一轮参数,不能重置为 0
- 建议设置超时或最大迭代次数(比如 10000 轮),防止因数据异常导致死循环
COUNT 参数不是“每次返回条数”,而是扫描粒度提示
COUNT n 表示 Redis 内部大约检查 n 个哈希槽位,并非精确返回 n 个 key。实际返回数量可能远少于 n(尤其在 key 分布稀疏时),也可能略多。默认值是 10,线上建议设为 100~500。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 太小(如 COUNT 10):网络往返多、效率低、客户端开销大
- 太大(如 COUNT 10000):单次耗时上升,虽仍不阻塞,但可能影响实时性
- 推荐根据业务容忍度调整:统计类任务可用 500,清理类任务可用 200 并配合 DEL 批量执行
必须做客户端去重,否则会误删或重复处理
SCAN 在 rehash 过程中可能返回重复 key(高位翻转算法 + 扩容迁移导致),这是协议层面的设计特性,无法避免。
- 用 HashSet
缓存已处理的 key,每次拿到新 batch 先过滤再操作 - 如果 key 总量极大(千万级),HashSet 内存占用高,可改用 BloomFilter 做概率去重(允许极小误差)
- 注意:去重应在内存中完成,不要依赖 Redis 的 EXISTS —— 那会引入额外网络开销和竞争
遍历中新增的 key 可能被跳过,按需补偿
SCAN 是弱一致性遍历:它只保证返回“遍历开始时已存在”的 key 的子集,期间新增的 key 可能落在已扫描过的桶里,从而漏掉。
- 若业务要求强完整性(如全量备份),需配合 TTL 扫描或时间戳字段二次校验
- 若用于清理临时 key(如 session:xxx),漏扫影响小,无需补偿
- 可加一层兜底:遍历结束后再跑一次 KEYS pattern(仅限低峰期、且 key 总量可控的小库)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










