Redis SCAN 为什么会返回重复键
Redis SCAN 返回重复键并不一定是 Redis 出错。它是游标式、弱状态的渐进遍历:一次调用只带回一小批结果,服务端只记住游标,不为客户端保存“已经见过哪些键”的集合。因此,同一个键在一次完整遍历中可能出现多次。真正需要修复的通常不是“让 SCAN 绝不重复”,而是让消费端能安全地重复处理。
只要从游标0开始,并持续使用上一轮返回的游标直到再次得到0,才能算完成一次完整遍历;业务侧必须接受重复、空批次和遍历期间数据变化。
- 重复返回是 SCAN 的已知语义,不等同于循环代码重复调用。
COUNT只影响每次工作的量,不是分页大小,也不能消除重复。- 删除、迁移、刷新 TTL 等副作用要做成幂等;需要严格一次时,把任务状态放到独立集合或表中。
先确认是重复返回,还是重复处理
排查时先给每轮响应记录 cursor、返回数量和键名,不要只看最终日志里的“处理次数”。下面的命令可以观察重复键,但它本身没有修改 Redis 数据:
SCAN 返回重复键的核心原因是 Redis 采用了增量迭代的设计,全程不做全表阻塞遍历,服务端不会主动过滤已经返回过的键,再配合哈希槽的扩容缩容、遍历中途发生键值变更等场景,就很容易出现重复输出的情况。这类特性是 Redis 官方明确说明的,业务侧自己做去重就能正常使用,不用额外修改服务端逻辑。
redis-cli SCAN 0 MATCH 'cache:user:*' COUNT 100
# 中文说明:从 0 开始,只筛选用户缓存键;COUNT 100 只是每轮工作量提示。
如果同一个键在不同游标批次出现,属于 SCAN 允许的重复返回;如果响应没有重复、业务表却重复写入,问题在消费端。另一个常见误判是把空数组当成结束:只要游标还不是 0,空批次也必须继续请求。

重复从哪里来:游标不是快照位置
SCAN 的游标描述的是服务端下一段遍历位置,不是固定页码,也不是某个键的唯一编号。底层字典发生扩容或缩容时,桶位置会重新分布;渐进式遍历跨越这些变化,就可能再次触达已经返回过的键。即使没有明显扩缩容,也不能把返回顺序当成稳定排序。
MATCH 是取出元素后的过滤条件,匹配很稀疏时,很多轮可能返回空结果;COUNT 是提示,服务端可以返回少于、多于甚至暂时为零的元素。小集合使用紧凑编码时,还可能一次返回全部元素。以上都不会改变“用游标直到 0”的结束条件。
| 现象 | 正确判断 | 处理方式 |
|---|---|---|
| 同一键出现两次 | 允许发生 | 消费端去重或保证幂等 |
| 某轮返回空数组 | 不代表结束 | 继续使用返回游标 |
| 改大 COUNT 后重复减少 | 只是批次变化 | 不能据此依赖唯一返回 |
| 遍历中新增或删除键 | 结果存在边界不确定性 | 限定窗口或接受最终一致 |
用游标闭环,并把副作用做成幂等
以 go-redis 为例,循环的关键是把本次返回的 nextCursor 原样交给下一次调用。示例用本地集合记录已处理键,避免一次任务中的重复副作用;集合过大时,应换成带任务编号和过期时间的外部存储。
var cursor uint64
seen := make(map[string]struct{})
for {
keys, nextCursor, err := rdb.Scan(ctx, cursor, "cache:user:*", 100).Result()
if err != nil {
return fmt.Errorf("扫描 Redis keyspace 失败: %w", err) // 中文说明:保留原错误,便于重试和告警。
}
for _, key := range keys {
if _, exists := seen[key]; exists {
continue // 中文说明:同一轮任务中重复出现时,不重复执行副作用。
}
seen[key] = struct{}{}
if err := refreshOne(ctx, key); err != nil {
return fmt.Errorf("刷新键 %q 失败: %w", key, err) // 中文说明:失败立即停止,避免静默漏处理。
}
}
cursor = nextCursor
if cursor == 0 {
break // 中文说明:只有服务端返回 0,完整遍历才结束。
}
}
如果任务是删除过期缓存,重复执行 UNLINK key 通常比“先查再删”更容易做成幂等;如果是写数据库,就用业务唯一键、任务批次号或 upsert 约束重复提交。严格的一次性语义不能由 SCAN 单独提供。

线上排查要看哪些边界
生产任务建议同时记录任务 ID、起始时间、最后游标、扫描总数、唯一键数、重复数、空批次数和失败键。这样可以区分“Redis 返回重复”和“客户端重试整批”两类问题。若 keyspace 持续增长,完整遍历可能迟迟回不到 0;可以限定维护窗口、冻结写入,或接受只覆盖某个时间段的最终一致结果。
Redis Cluster 下,逻辑上的全量扫描还意味着分别遍历各个主节点,应用层需要为节点维护独立游标和去重边界;不要把一个节点的游标直接交给另一个节点。若只是按前缀清理,优先缩小 MATCH 范围并控制并发,避免把“重复键”误判成需要不断增大 COUNT 的性能问题。
常见问题
把 COUNT 调大能彻底消除重复吗?
不能。它只是服务端每次调用的工作量提示,返回数量和重复语义都没有变。
返回空数组是不是已经扫描完了?
不是。只有返回游标为 0 才结束;空数组且游标非零时必须继续。
需要严格一次处理,应该依赖 SCAN 吗?
不应该。把唯一性放到集合、数据库唯一键或任务状态表中,并让副作用支持重试。
把 SCAN 当作“可能重复、可能空批、非快照”的遍历接口,消费端再负责幂等和任务记录,重复键就会从故障信号变成可管理的正常边界。
漫千绘怎么管理心情书架?漫读日记与阅读偏好说明
- 上一篇
- 漫千绘怎么管理心情书架?漫读日记与阅读偏好说明
- 下一篇
- Go 大文件怎么流式转换成 Base64
-
- 数据库 · Redis | 3小时前 |
- Redis 统计 UV 怎么用 HyperLogLog:误差和适用场景
- 302浏览 收藏
-
- 数据库 · Redis | 4小时前 | Redis · ZSET · 排行榜 · zset Redis排行榜 Sorted Set
- Redis 排行榜分数相同时怎么安排排序
- 279浏览 收藏
-
- 数据库 · Redis | 1天前 |
- Redis Stream 消费者挂掉后消息卡在 PEL:用 XAUTOCLAIM 做可重复的接管流程
- 345浏览 收藏
-
- 数据库 · Redis | 1天前 |
- Redis Lua 长脚本怎么拆:保留短状态变更并控制单次批量
- 216浏览 收藏
-
- 数据库 · Redis | 1天前 | 消息队列 · Redis教程 · 故障恢复 · 消费组 · redis 消费组 Redis Streams XNACK Pending 消息
- Redis XNACK 如何快速交回 Pending 消息:消费者退出与重投优先级
- 332浏览 收藏
-
- 数据库 · Redis | 2天前 | Redis · 性能 · 集合 · 数据统计 · 基数统计 Redis 8.10 SUNIONCARD 集合并集
- Redis 8.10 SUNIONCARD 为什么不返回成员:集合并集计数的低开销用法
- 490浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 157次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 86次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 46次使用
-
- PromptHero
- PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
- 26次使用
-
- Stable Diffusion Prompt Book
- 深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
- 29次使用
-
- Redis XAUTOCLAIM 之后为什么仍有 pending:JUSTID、PEL 与消息删除边界
- 2026-08-29 501浏览
-
- Redis SET 的 GET 与 KEEPTTL 怎么一起验收:旧值返回、续期与回滚边界
- 2026-08-20 501浏览
-
- Redis 慢命令快照小工具:用 SLOWLOG 定位接口延迟
- 2026-06-29 501浏览
-
- Redis集群节点规划与部署全解析
- 2025-08-02 501浏览
-
- 多线程Redis优化技巧分享
- 2025-06-29 501浏览

