当前位置:首页 > 文章列表 > 数据库 > Redis > Redis SCAN 为什么会返回重复键

Redis SCAN 为什么会返回重复键

来源:17golang原创 2026-09-06 01:15:58 0浏览 收藏

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,空批次也必须继续请求。

Redis SCAN 游标遍历中哈希表、游标批次与可能重复键的静态关系
图1:Redis SCAN 的游标、键空间和返回批次之间是弱状态关系;同一键可能出现在多个批次。

重复从哪里来:游标不是快照位置

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 单独提供。

Redis SCAN 返回游标进入幂等消费者后连接去重集合与可重试副作用的静态关系
图2:把 SCAN 的返回批次交给幂等消费者,去重状态与可重试副作用应处在独立边界。

线上排查要看哪些边界

生产任务建议同时记录任务 ID、起始时间、最后游标、扫描总数、唯一键数、重复数、空批次数和失败键。这样可以区分“Redis 返回重复”和“客户端重试整批”两类问题。若 keyspace 持续增长,完整遍历可能迟迟回不到 0;可以限定维护窗口、冻结写入,或接受只覆盖某个时间段的最终一致结果。

Redis Cluster 下,逻辑上的全量扫描还意味着分别遍历各个主节点,应用层需要为节点维护独立游标和去重边界;不要把一个节点的游标直接交给另一个节点。若只是按前缀清理,优先缩小 MATCH 范围并控制并发,避免把“重复键”误判成需要不断增大 COUNT 的性能问题。

常见问题

把 COUNT 调大能彻底消除重复吗?

不能。它只是服务端每次调用的工作量提示,返回数量和重复语义都没有变。

返回空数组是不是已经扫描完了?

不是。只有返回游标为 0 才结束;空数组且游标非零时必须继续。

需要严格一次处理,应该依赖 SCAN 吗?

不应该。把唯一性放到集合、数据库唯一键或任务状态表中,并让副作用支持重试。

把 SCAN 当作“可能重复、可能空批、非快照”的遍历接口,消费端再负责幂等和任务记录,重复键就会从故障信号变成可管理的正常边界。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
漫千绘怎么管理心情书架?漫读日记与阅读偏好说明漫千绘怎么管理心情书架?漫读日记与阅读偏好说明
上一篇
漫千绘怎么管理心情书架?漫读日记与阅读偏好说明
Go 大文件怎么流式转换成 Base64
下一篇
Go 大文件怎么流式转换成 Base64
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    579次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    619次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    650次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    615次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    431次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码