首页/数据/Redis 进阶速查/SCAN 游标遍历替代 KEYS
性能与排障129 条

SCAN 游标遍历替代 KEYS

SCAN 用游标分批迭代键空间,每次返回一批与下一个游标,直到游标为 0;它不长时间阻塞服务,是生产环境枚举键的标准方式。

Redis 进阶速查数据更新于 2026-10-13

SCAN 是什么

SCAN 是 Redis 提供的游标式增量遍历命令:每次调用只扫描一部分键并返回一个游标,调用方带上新游标继续下一轮,直到游标回到 0 表示遍历结束。它把大范围的枚举工作切成很多次轻量调用,中间可以插入其他请求,因此不会像 KEYS 那样长时间独占主线程。生产环境枚举键空间的唯一正确方式就是 SCAN 家族。

基本信息

语法SCAN cursor [MATCH pattern] [COUNT n] [TYPE type]
结束条件返回的游标为 0
COUNT 语义每批工作的提示值,不是返回条数上限,也不保证下限
TYPE 过滤TYPE string|hash|list|set|zset|stream,需 6.0 及以上
性能单次调用复杂度随 COUNT 变化,总体为 O(N),不阻塞服务

典型使用场景

  • 批量清理某前缀的历史键,边扫边删并把无关键跳过,避免一次性删除引发大量阻塞
  • 数据盘点与审计:统计某类键的数量、抽样检查 TTL 与编码是否符合预期
  • 迁移或重建:把符合模式的键逐个读到应用侧,再写入新的数据结构或新实例
  • 容量治理:结合 TYPE 过滤找出大 key、无 TTL 的键、本该淘汰却仍在的键
  • 定时任务里做渐进式修复,把一次重活拆到多轮扫描之间,削掉尖峰

常用示例

常用示例
redis-cli SCAN 0 MATCH user:* COUNT 1000     # 首轮扫描,COUNT 只是提示
redis-cli SCAN 12345 MATCH user:* COUNT 1000 # 用返回的游标继续
redis-cli SCAN 0 COUNT 1000 TYPE hash        # 只找 hash 类型的键
redis-cli HSCAN user:1001 0 COUNT 100        # 遍历单个 Hash 的字段
redis-cli SSCAN tags 0 MATCH 'go*'           # 遍历 Set 成员
redis-cli ZSCAN board 0 COUNT 100            # 遍历 ZSet 成员与分数
redis-cli --scan --pattern 'user:*'          # redis-cli 封装的循环扫描

SCAN 家族对照

命令遍历对象返回内容
SCAN当前库的全部键键名
HSCAN指定 Hash字段与值成对返回
SSCAN指定 Set成员
ZSCAN指定 ZSet成员与其分数成对返回
MATCH过滤模式glob 风格,如 user:* 与 a?c
SCAN 的保证与不保证:保证遍历期间一直存在、从头到尾没被删过的键一定会被返回;不保证返回结果不重复,也不保证遍历期间新增或删除的键被如何对待。因此消费端必须能容忍重复,实现上常用幂等写入或集合去重。
COUNT 的作用是调节每批工作量:值太小会导致往返次数暴增、总耗时变长;值太大会让单次调用变重,靠近 KEYS 的阻塞表现。默认 10 偏保守,线上批量任务常用 100 到 1000,并在压测环境确认单次调用的耗时。
不要在业务高峰期对全库执行 KEYS:KEYS 是 O(N) 且一次性扫描全部键,会阻塞主线程直到扫完,几百万键的实例上足以造成秒级停顿并引发超时雪崩。HGETALL、SMEMBERS、LRANGE key 0 -1 对超大集合有同样的风险。

易错点

  • 把 SCAN 当成快照一致性遍历:遍历期间新增的键可能被扫到也可能不被扫到,需要精确统计时应改用落地明细表或事件流
  • 假定游标是递增数字并自行做加减或合并,游标是按位反转规律生成的二进制状态,唯一合法用法是原样回传
  • 按 COUNT 假设每批返回条数来写分页逻辑,实际返回条数可多可少,分页应由游标驱动而非偏移量
  • 边 SCAN 边大量删除导致某些键被跳过,若要求全量覆盖,应在删除轮次结束后再补一轮扫描核对

数据来源与参考

  • Redis 官方文档:SCAN 命令(含保证与限制说明)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌