SCAN是Redis 2.8+唯一生产级安全遍历命令,通过游标分批迭代避免阻塞主线程、控制内存占用;它仅支持MATCH字符串匹配,不支持原生命令级类型过滤,需配合TYPE或依赖业务前缀(如user:*)缩小范围。

SCAN命令是唯一可行的生产级方案
直接用 KEYS * 在任何稍具规模的 Redis 实例上都等于主动触发服务抖动——它会阻塞主线程,直到全库扫描完成。而 SCAN 是 Redis 2.8+ 唯一被官方设计用于安全遍历的命令,它不保证原子性,但能避免阻塞、控制内存占用、适配单线程模型。
为什么不能靠类型过滤,只能靠业务前缀
SCAN 本身不支持按数据类型(如 string / hash / list)筛选键,它的 MATCH 参数只做字符串模式匹配。Redis 没有内置机制在游标遍历时判断每个 key 的 type,必须靠客户端逐个调用 TYPE 命令确认——这会显著放大网络往返和 CPU 开销。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 实际中,99% 的 String 键都有明确业务前缀(如
user:123、cache:order:),应优先用MATCH user:*或MATCH cache:*缩小范围 - 若真需严格限定为 string 类型,必须在
SCAN返回每批 key 后,用 pipeline 批量执行TYPE,再过滤出string - 注意:
TYPE返回的是字符串值(如"string"),不是数字或布尔,比对时要写死字符串
Jedis 中 SCAN 的关键参数陷阱
Java 用 Jedis 调用 SCAN 时,COUNT 不是硬限制,而是提示值;游标处理不当会导致漏键或无限循环。
-
ScanParams必须显式设置.match("*"),否则默认不匹配任何 key(Jedis 4.x 行为) -
COUNT建议设为 100–500:太小(如 10)导致请求次数爆炸;太大(如 10000)可能使单次响应包超限或触发 Redis 输出缓冲区限制 - 游标必须用
scanResult.getCursor()获取,不能用scanResult.getStringCursor()(后者在某些版本返回 null) - 循环终止条件只能是
"0".equals(cursor),不能用cursor == "0"(字符串引用比较失效)
真正落地时容易被忽略的三件事
SCAN 不是“开箱即用”的银弹,工程落地时这几个点常被跳过,却直接决定是否稳定:
- 连接必须复用:每次新建
Jedis实例再 scan,会快速耗尽连接池;应在连接池内复用连接 - 超时要设长:SCAN 单次虽快,但整轮遍历可能持续数秒甚至分钟,
socketTimeout和connectionTimeout至少设为 30s - 结果去重不可省:SCAN 在遍历中 key 被删除或新增时,可能重复返回同一 key(概率低但存在),业务侧需用
Set缓存已处理 key

















