一、SCAN 命令的核心价值
Redis 的 KEYS *
命令虽然可以遍历所有键,但在生产环境中直接使用可能导致服务阻塞(时间复杂度 O(n))。SCAN 命令通过游标分批次迭代,实现非阻塞式遍历,成为处理百万级键的安全选择。
二、命令语法与参数解析
1. 基础语法
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
2. 参数说明
参数 | 作用 |
---|
cursor | 游标值,首次传入 0,后续使用前次返回的游标 |
MATCH | 模式匹配,如 user:* 过滤以 “user:” 开头的键 |
COUNT | 建议返回数量(默认 10),实际返回可能不同,不影响迭代完整性 |
TYPE | 指定键类型(Redis 6.0+),如 string 、hash 、list 等 |
三、使用示例
1. 基础迭代
127.0.0.1:6379> SCAN 0
1) "17"
2) 1) "user:1001"
2) "product:2023"
3) "session:abcd"
127.0.0.1:6379> SCAN 17
1) "0"
2) 1) "order:5678"
2. 结合 MATCH 和 COUNT
127.0.0.1:6379> SCAN 0 MATCH prod* COUNT 50
1) "23"
2) 1) "prod:100"
2) "prod:101"
3. 指定键类型(Redis 6.0+)
127.0.0.1:6379> SCAN 0 TYPE hash
1) "5"
2) 1) "user:profile:1001"
2) "product:meta:2023"
四、SCAN 的核心特点
1. 非阻塞迭代
- 原理:基于字典槽(slot)分批次遍历,每批耗时 O(1)
- 优势:避免单次操作长时间阻塞主线程
2. 不保证完全一致性
- 表现:迭代期间新增/删除的键可能被包含或遗漏
- 原因:采用“快照”机制,但非强一致性视图
3. 可能返回重复键
- 概率:约 10%-20% 的重复率(取决于数据修改频率)
- 处理:客户端需自行去重
4. 适用场景
- 统计键数量、导出匹配模式的键
- 定期清理过期数据(结合
TTL
检查) - 大数据量的键空间分析
五、SCAN 家族命令
命令 | 作用 | 示例 |
---|
SSCAN | 遍历集合元素 | SSCAN myset 0 MATCH a* |
HSCAN | 遍历哈希表字段 | HSCAN user:1001 0 |
ZSCAN | 遍历有序集合成员 | ZSCAN rankings 0 |
六、注意事项与最佳实践
1. COUNT 参数调优
- 小值(如 10):适合网络延迟敏感场景
- 大值(如 1000):适合内网低延迟环境
- 建议:根据平均键大小和网络条件动态调整
2. 客户端去重方案
seen = set()
cursor = 0
while True:
cursor, keys = redis.scan(cursor, match='user:*', count=100)
for key in keys:
if key not in seen:
process(key)
seen.add(key)
if cursor == 0:
break
3. 避免的常见错误
- 游标持久化:不要存储游标(可能失效)
- COUNT 误解:实际返回数量可能大于设定值
- 超时处理:添加超时机制防止无限循环
七、性能对比(10万键测试)
操作 | 耗时 | CPU 峰值 | 内存波动 |
---|
KEYS * | 320ms | 95% | 50MB↑ |
SCAN | 15ms | 15% | 2MB↑ |
总结
SCAN 命令是 Redis 高可用设计的典范,通过游标分批、非阻塞式遍历,完美平衡了数据遍历需求与服务稳定性。合理运用 MATCH
过滤、COUNT
调优及客户端去重,可高效应对海量数据场景。在需要精确一致性的场景中,仍需谨慎评估或结合事务处理。