首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Redis SCAN 巡检避坑:空批次、重复键与集群扫描边界

Redis SCAN 巡检避坑:空批次、重复键与集群扫描边界

原创
作者头像
一只特立独行的兔先生
发布于 2026-10-19 19:25:35
发布于 2026-10-19 19:25:35
210
举报

结论:SCAN 是渐进遍历,不是分页查询

用 Redis 的 SCAN 做键巡检,最容易写错的不是连接代码,而是结束条件和统计口径。返回空数组不代表结束,COUNT 不是每页条数,遍历期间还可能读到重复键。换成集群后,还必须确认扫描覆盖的是一个节点还是整个实例。

下面给出一套只读巡检流程,适合先在测试实例验证,再迁移到生产维护窗口。它不读取业务值,也不执行删除;输出的是观察次数,不冒充某一时刻的精确键总数。

先确认扫描范围

原生 Redis Cluster 的一次节点级 SCAN 不会自动遍历所有主节点,客户端支持集群重定向,也不等于支持全局遍历。

腾讯云分布式缓存数据库(兼容 Redis)的官方《集群架构全局 SCAN 使用指南》说明:通过 Proxy 扩展提供全局扫描,要求 Proxy 版本为 5.8.9 或以上。不要把 Redis 内核版本当成 Proxy 版本;接入前要核对实例架构、接入方式以及对应产品文档。

下文 Python 示例只适用于单节点,或已确认支持全局 SCAN 的代理端点。原生 Cluster 需要额外设计逐主节点遍历与拓扑变化处理,不能直接套用此脚本宣称“全实例扫描完毕”。

正确理解三个参数

命令形式如下:

SCAN cursor MATCH pattern COUNT count

  • 首次传入游标 0,后续原样传入服务端返回的游标;不能自行加一,也不能计算完成百分比。
  • 只有返回游标为 0,才表示这一轮结束;最后一批结果仍须处理。
  • COUNT 是工作量提示,不保证返回指定数量。MATCH 用于过滤结果,通常不是前缀索引;匹配少不等于遍历成本低。

因此,if not keys: break 会漏扫。直接累加 len(keys) 则可能把重复键计算多次。

一个有时间边界的只读脚本

准备 Python 与 redis-py,把连接信息放入受控环境变量 REDIS_URL。地址格式为 redis:// 或启用 TLS 时的 rediss://,认证信息含特殊字符时须按 URL 规则编码。不要将真实连接串粘贴到工单、代码仓库或终端录屏里。

代码语言:bash
复制
python -m pip install redis
python scan_audit.py

保存以下内容为 scan_audit.py:

代码语言:python
复制
import os
import time
import redis

client = redis.Redis.from_url(
    os.environ["REDIS_URL"],
    socket_connect_timeout=3,
    socket_timeout=3,
    decode_responses=False,
)
cursor = 0
batches = 0
observations = 0
started = time.monotonic()
deadline = started + 60

try:
    while True:
        if time.monotonic() >= deadline:
            raise TimeoutError("scan incomplete: time budget exceeded")
        cursor, keys = client.scan(
            cursor=cursor,
            match=b"audit:demo:*",
            count=100,
        )
        batches += 1
        observations += len(keys)
        # 空批次也必须继续;结束批次也必须先统计。
        if cursor == 0:
            break
        time.sleep(0.02)
    print({
        "completed": True,
        "batches": batches,
        "observations": observations,
        "elapsed_seconds": round(time.monotonic() - started, 3),
    })
finally:
    client.close()

100、20ms 和 60s 都是示例起点,不是平台推荐阈值。时间预算在批次之间检查,并不是硬实时中断;单次请求仍受连接、读取超时及客户端行为影响。脚本遇到服务端错误会失败退出,不会打印成功结果。

使用二进制响应可避免非 UTF-8 键名导致解码失败。脚本不保留全量键集合,因此不会为了去重而额外占用与键数量成正比的客户端内存。

如何验收,而不是只看“没有报错”

在独立测试库中,用唯一前缀准备三个没有过期时间的键,例如 audit:demo:1、audit:demo:2、audit:demo:3,扫描期间停止对该前缀的写入。在测试版处理分支中收集实际键名,去重后应恰好等于这三个键;不要断言某个键在第几批返回。

再把匹配条件改为确定不存在的前缀。允许出现多个空批次,但仍必须等返回游标为零才成功。扩大测试数据量后,可以观察 COUNT 改变对批次数与耗时的影响,不应断言每批恰好返回 100 条。

生产验证还要观察业务延迟、CPU、请求量和网络流量。只读不等于无成本;批间休眠只能降低客户端发起速率,不能保证单条命令延迟。

集群切换与重复数据的处理边界

腾讯云官方文档指出,主从切换或缩容可能触发 invalid cursor(master node idx out of range)。不能吞掉异常并继续使用旧游标。上面的脚本选择直接终止,把本轮标记为不完整。

需要自动重扫时,应从零开始新一轮,设置最大重试次数,并明确旧轮统计是否丢弃。若扫描结果会触发外部任务,还必须处理重复执行。仅凭键名去重也无法区分同名键被删除后重新创建的情况,因此不能随意把只读巡检改成批量删除工具。

最后要区分两个目标:游标归零证明遍历协议完成,不证明拿到了时点快照。持续写入场景下,精确盘点应另行设计一致性边界;不要把一次 SCAN 的累计观察数当成财务式精确报表。

参考文档

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 结论:SCAN 是渐进遍历,不是分页查询
  • 先确认扫描范围
  • 正确理解三个参数
  • 一个有时间边界的只读脚本
  • 如何验收,而不是只看“没有报错”
  • 集群切换与重复数据的处理边界
  • 参考文档
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档