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 返回的新游标,返回 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 都不能提前结束。一个键也可能因为底层哈希表调整而被返回多次,所以删除、迁移、补偿等动作要设计成幂等,或在应用侧维护去重集合。

MATCH 和 COUNT 只能改变返回方式
MATCH 是 glob 风格的过滤条件,但过滤发生在元素被扫描之后。匹配很少时,连续几轮返回空集合并不异常。不要因为某一轮为空就认为遍历完成,也不要把 MATCH 当成降低整个键空间扫描工作的索引。
COUNT 默认值是 10,作用是提示每轮投入多少工作量。它不是保证返回 100 个键的分页参数,Redis 允许某轮返回更少、更多,甚至返回空集合。生产任务应按延迟和服务器负载调节它,而不是用返回数量判断是否结束。
| 现象 | 正确判断 | 处理方式 |
|---|---|---|
| 本轮返回空列表 | 游标可能仍未归零 | 继续使用返回游标 |
| 同一键出现两次 | 属于允许的重复 | 让处理动作幂等,必要时去重 |
| 希望严格按创建时间分页 | SCAN 不提供排序快照 | 改用有序集合、数据库分页或专门索引 |
动态键空间里什么时候不适合用 SCAN
SCAN 适合把一次可能阻塞很久的全库操作拆成多次小调用,但它仍然要付出完整遍历的总成本。若业务在遍历期间持续高速写入,键空间可能一直增长,Redis 官方也明确说明这种情况下完整遍历不一定能终止。
因此,定时清理任务可以接受“遍历期间的近似时点”和重复处理;需要严格一致的名单、稳定排序或一次性快照时,就不该只靠 SCAN。可以在写入时维护集合或有序集合,给任务建立明确的边界时间,并把处理结果写入幂等记录,再用 SCAN 做补偿扫描。
常见问题
SCAN 看到游标 0 就一定没有键了吗?
游标回到 0 表示这一轮遍历结束,不表示数据库为空,也不表示遍历期间变化过的键都被纳入了同一快照。
COUNT 100 能保证每次返回 100 个键吗?
不能。COUNT 是实现层面的工作量提示,返回数量可能少于、接近或不同于这个值,空返回也可能出现。
如何避免重复处理?
优先把删除、刷新或迁移动作设计成幂等;如果任务必须在单轮内去重,再用集合记录已经处理的键,但要评估额外内存和任务时长。
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
195 收藏
-
313 收藏
-
484 收藏
-
104 收藏
-
463 收藏
-
166 收藏
-
250 收藏
-
204 收藏
-
244 收藏
-
173 收藏
-
458 收藏
-
306 收藏
-
146 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习