登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis SCAN 为什么不能当作一次性全量遍历

来源:17golang原创

时间:2026-10-23 05:03:38 236浏览 收藏

Redis 的 SCAN 不能当成“一次请求就拿到全量键”的快照查询。它是游标式增量遍历:从游标 0 开始,把本次返回的游标交给下一次请求,直到返回游标再次变成 0,这才叫完成一次完整遍历。

官方地址:https://redis.io/

要点速览
  • 单次 SCAN 返回的是一批键和下一游标,不承诺是完整列表、固定数量或稳定顺序。
  • 完整遍历能覆盖从开始到结束都持续存在的键,但同一个键可能返回多次,应用必须允许重复处理。
  • MATCH 和 COUNT 都不能把键空间变成快照;键空间持续增长时,完整遍历也可能无法结束。

把 SCAN 看成游标迭代而不是快照

我在清理带前缀的缓存键时最容易踩的坑,是把第一条 SCAN 0 MATCH cache:* 的结果直接当成“所有匹配键”。这只代表游标的一小段位置。Redis 官方定义的完整遍历,是从 0 开始,并持续使用上一轮返回的游标,直到返回 0。

Redis 键空间、SCAN 游标、返回键集合和下一次请求之间的静态关系说明图
图1:SCAN 游标与返回键集合的关系说明图,不是运行截图或全量快照。

最小的命令循环可以这样写:

# 每轮沿用 Redis 返回的新游标,返回 0 才结束完整遍历
cursor=0
while :; do
  # COUNT 是工作量提示,不是严格的分页大小
  reply=$(redis-cli --raw SCAN "$cursor" MATCH 'cache:*' COUNT 100)
  # 实际项目中应使用客户端的结构化返回值解析游标和键列表
  next_cursor=$(printf '%s\n' "$reply" | sed -n '1p')
  printf '%s\n' "$reply" | sed '1d'
  [ "$next_cursor" = "0" ] && break
  cursor="$next_cursor"
done

示例用 shell 展示游标关系;正式代码应优先使用 Redis 客户端的数组返回值,避免靠换行拆解键名。重点不是一次拿多少,而是不能丢掉下一游标。

完整遍历能保证什么,又不保证什么

如果一个键从遍历开始到结束始终存在,完整遍历保证它至少会在某次返回中出现;如果一个键在开始前已经删除且整个遍历期间没有重新出现,也不会被返回。但这不是快照语义:遍历期间新增或删除的键,是否出现没有同样的确定性。

另外,SCAN 的返回顺序没有业务意义,单次调用可以返回空集合,直到游标回到 0 都不能提前结束。一个键也可能因为底层哈希表调整而被返回多次,所以删除、迁移、补偿等动作要设计成幂等,或在应用侧维护去重集合。

Redis SCAN 稳定存在、重复、动态变化、MATCH、COUNT 与幂等处理之间的静态边界说明图
图2:SCAN 完整遍历保证与动态键空间边界说明图,不是监控截图。

MATCH 和 COUNT 只能改变返回方式

MATCH 是 glob 风格的过滤条件,但过滤发生在元素被扫描之后。匹配很少时,连续几轮返回空集合并不异常。不要因为某一轮为空就认为遍历完成,也不要把 MATCH 当成降低整个键空间扫描工作的索引。

COUNT 默认值是 10,作用是提示每轮投入多少工作量。它不是保证返回 100 个键的分页参数,Redis 允许某轮返回更少、更多,甚至返回空集合。生产任务应按延迟和服务器负载调节它,而不是用返回数量判断是否结束。

现象正确判断处理方式
本轮返回空列表游标可能仍未归零继续使用返回游标
同一键出现两次属于允许的重复让处理动作幂等,必要时去重
希望严格按创建时间分页SCAN 不提供排序快照改用有序集合、数据库分页或专门索引

动态键空间里什么时候不适合用 SCAN

SCAN 适合把一次可能阻塞很久的全库操作拆成多次小调用,但它仍然要付出完整遍历的总成本。若业务在遍历期间持续高速写入,键空间可能一直增长,Redis 官方也明确说明这种情况下完整遍历不一定能终止。

因此,定时清理任务可以接受“遍历期间的近似时点”和重复处理;需要严格一致的名单、稳定排序或一次性快照时,就不该只靠 SCAN。可以在写入时维护集合或有序集合,给任务建立明确的边界时间,并把处理结果写入幂等记录,再用 SCAN 做补偿扫描。

常见问题

SCAN 看到游标 0 就一定没有键了吗?

游标回到 0 表示这一轮遍历结束,不表示数据库为空,也不表示遍历期间变化过的键都被纳入了同一快照。

COUNT 100 能保证每次返回 100 个键吗?

不能。COUNT 是实现层面的工作量提示,返回数量可能少于、接近或不同于这个值,空返回也可能出现。

如何避免重复处理?

优先把删除、刷新或迁移动作设计成幂等;如果任务必须在单轮内去重,再用集合记录已经处理的键,但要评估额外内存和任务时长。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>