news 2026/9/27 19:24:44

Redis 前缀批量查 key 实战:用 SCAN 替代 KEYS 的配置骨架与验证步骤

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis 前缀批量查 key 实战:用 SCAN 替代 KEYS 的配置骨架与验证步骤

1. 线上一个 KEYS 把 Redis 拖垮的真实场景

先说结论:在 Redis 里按前缀批量查 key,能用SCAN就别用KEYS。KEYS pattern这个命令看起来最直观,KEYS user:*一把梭,但它有两个致命问题:一是时间复杂度是 O(N),N 是整个库的 key 总数,不是匹配到的数量;二是 Redis 处理命令是单线程的,KEYS执行期间会阻塞其他所有请求,key 越多卡得越久。

我遇到过最典型的一次,是某个业务方在监控面板上想统计一下order:cache:*到底有多少条,直接在客户端敲了KEYS order:cache:*。当时那个实例大概 800 万 key,命令发出去之后,整个实例的 P99 从 2ms 飙到 3 秒多,上游接口大面积超时,最后是靠重启才缓过来。从那以后我们内部直接把KEYS加进了命令黑名单,用rename-command禁掉。

那按前缀查 key 这个需求本身是合理的,比如清理某类缓存、做数据迁移、排查脏数据,都需要按前缀捞一批 key。正确的做法是用SCAN的游标遍历:它每次只返回一小批,不阻塞主线程,客户端拿着游标一轮轮翻,直到游标回到 0 表示遍历结束。代价是它不保证强一致快照,遍历过程中数据被改动可能漏掉或重复,但对绝大多数「按前缀找 key」的场景完全够用。

这篇就围绕SCAN讲清楚三件事:命令参数怎么配、MATCH前缀骨架怎么写、COUNT怎么调,最后给你一套用redis-cli验证遍历完整性和阻塞耗时的具体动作。如果你在写代码时需要调用模型能力来辅助生成或审查这类脚本,可以顺手用 TaoToken 的模型对话做对照,地址是 https://taotoken.net/api ,模型对话入口在 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model-chat 。

2. SCAN 命令骨架与 COUNT、MATCH 参数拆解

SCAN的完整语法是:

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

四个部分逐个说。

cursor是游标,第一次调用传0,之后每次传上一次返回的游标值。当返回的游标又是0时,说明这一轮遍历结束。注意游标不是「第几个 key」这种偏移量,它是一个内部哈希表的反向二进制迭代游标,所以你会看到它跳来跳去,比如 0 → 17 → 288 → 224,这是正常的,别试图去理解它的数值规律。

MATCH pattern是模式匹配,支持 glob 风格通配符,*匹配任意字符,?匹配单个字符,[abc]匹配字符集。按前缀查就是MATCH user:*这种写法。这里有个关键点必须记住:MATCH 是在元素被扫描出来之后才做过滤的,不是先按 pattern 去定位数据。也就是说COUNT 10表示「这次从哈希表里扫 10 个槽位」,扫出来的这 10 个里可能一个都不匹配user:*,于是你看到返回空列表但游标还在往前走。这就是为什么小 COUNT 配窄前缀时,会连续好几次返回空结果。

COUNT count是每次扫描的工作量提示,默认 10。官方文档明确说它只是个 hint,实际返回条数可能比 COUNT 多也可能少,取决于内部编码(比如 ziplist、intset、hashtable 的槽位分布)。所以别把 COUNT 当成「精确返回 N 条」。

TYPE type是 Redis 6.0 之后加的,可以只扫指定类型的 key,比如TYPE string,能进一步减少无效匹配,前缀 + 类型双过滤时很有用。

一个最小可用的前缀遍历骨架长这样:

# 第一轮,游标从 0 开始 SCAN 0 MATCH user:* COUNT 1000 # 假设返回游标 512 SCAN 512 MATCH user:* COUNT 1000 # 继续,直到返回的游标为 0

用 shell 写一个自动翻页的循环,方便在redis-cli里直接跑:

#!/bin/bash # scan_prefix.sh 用法: ./scan_prefix.sh "user:*" 1000 PATTERN="${1:-*}" COUNT="${2:-1000}" CURSOR=0 TOTAL=0 while :; do # 用 --raw 去掉引号,方便解析 RESULT=$(redis-cli --raw SCAN "$CURSOR" MATCH "$PATTERN" COUNT "$COUNT") CURSOR=$(echo "$RESULT" | head -n 1) KEYS=$(echo "$RESULT" | tail -n +2) if [ -n "$KEYS" ]; then echo "$KEYS" N=$(echo "$KEYS" | wc -l) TOTAL=$((TOTAL + N)) fi if [ "$CURSOR" = "0" ]; then break fi done echo "---- total matched: $TOTAL ----" >&2

这段脚本的核心就是「拿游标 → 取结果 → 判断游标是否为 0」。--raw参数很关键,不加的话redis-cli会给每个元素加引号,解析起来很烦。

3. COUNT 调优:为什么 1000 是个常用起点

COUNT 的取值直接决定两件事:网络往返次数和单次阻塞时长。COUNT 太小,比如默认的 10,800 万 key 的库你要来回 80 万次,网络 RTT 累加起来非常可观,而且每次返回空结果的概率高,客户端循环空转。COUNT 太大,比如直接上 10 万,单次SCAN内部要扫的槽位多,虽然不像KEYS那样一次性全扫,但单次耗时也会拉长,阻塞风险上升。

我的经验值是这样:

场景建议 COUNT理由
前缀命中率高(如user:*占大半)500 ~ 1000每次都能返回不少结果,往返次数少
前缀命中率低(如tmp:lock:*很稀疏)2000 ~ 5000抵消 MATCH 后置过滤带来的空返回
结果集预期 1 万以内直接设为预期大小一轮基本扫完,最省事
超大库 + 在线业务500 起步,观察耗时再调优先保证不阻塞

有个常见误区:以为 COUNT 是「返回结果条数」。不是的,COUNT 是「扫描的槽位数量」,MATCH 过滤后返回的才是结果。所以前缀越稀疏,COUNT 越要往大调,否则你会看到连续十几次空返回。

另外 COUNT 每次调用可以不一样,只要游标接得上就行。比如第一轮用 1000 探路,发现返回结果很少,后面几轮直接提到 5000,完全合法。

4. 用 redis-cli 验证遍历完整性与阻塞耗时

光会写命令不够,得能验证「遍历全不全」和「到底阻不阻塞」。下面这套动作可以直接照做。

4.1 造测试数据

先灌一批带前缀的 key,方便验证:

# 灌 10000 个 user: 前缀的 key for i in $(seq 1 10000); do redis-cli SET "user:$i" "v$i" > /dev/null done # 再灌 5000 个 order: 前缀的 key 做干扰 for i in $(seq 1 5000); do redis-cli SET "order:$i" "v$i" > /dev/null done

4.2 验证遍历完整性

用DBSIZE拿到总数,再用SCAN遍历user:*,对比数量:

redis-cli DBSIZE # 预期 15000 # 用第 2 节的脚本遍历 ./scan_prefix.sh "user:*" 1000 | wc -l # 预期 10000(脚本最后一行 total 输出到了 stderr,不影响 wc)

如果数量对得上,说明遍历完整。如果对不上,先检查是不是遍历过程中有写入,SCAN不保证快照一致,边写边扫可能漏。

4.3 验证阻塞耗时

这是最关键的一步,对比KEYS和SCAN的耗时。用redis-cli --latency开一个窗口持续观察延迟,另一个窗口执行命令:

# 窗口 A:持续观察延迟 redis-cli --latency # 窗口 B:执行 KEYS,观察窗口 A 的延迟尖刺 redis-cli KEYS "user:*" > /dev/null # 窗口 B:执行 SCAN 单轮,观察窗口 A 是否平稳 redis-cli SCAN 0 MATCH "user:*" COUNT 1000 > /dev/null

实测下来,KEYS那一瞬间窗口 A 的延迟会明显跳高,而SCAN单轮基本看不到尖刺。你也可以用redis-cli --intrinsic-latency 100看基线,再在执行命令时对比。

更严谨一点,用INFO commandstats看命令耗时统计:

redis-cli INFO commandstats | grep -E "cmdstat_keys|cmdstat_scan"

cmdstat_keys的usec_per_call会明显高于cmdstat_scan,而且keys的calls通常很少(因为被禁了),scan的调用次数多但单次耗时低。

5. 本篇常见错排查

报错一:ERR unknown command 'SCAN'或NOPERM this user has no permissions to run the 'scan' command

前者说明 Redis 版本太老(2.8 以下),升级即可。后者是 ACL 权限问题,Redis 6 之后默认用户可能没给scan权限,需要管理员授权:

redis-cli ACL SETUSER myuser +scan +dbsize

报错二:遍历结果比预期少

最常见原因是遍历期间有 key 被删除或新增。SCAN是弱一致遍历,不保证快照。如果业务要求精确,要么在低峰期做,要么用DUMP+RESTORE做快照副本再扫。另一个原因是MATCH写错了,比如user*漏了冒号,会匹配到username这类无关 key。

报错三:连续多次返回空列表,以为命令坏了

这是MATCH后置过滤的正常现象,不是 bug。把COUNT调大,或者接受空返回继续翻游标。判断是否结束只看游标是否为 0,不要看结果是否为空。

报错四:客户端循环里游标类型搞错

SCAN返回的游标是字符串,有些客户端库返回的是数字,比较时要注意类型。比如 Jedis 里ScanParams.SCAN_POINTER_START是字符串"0",别拿int 0去比。Python 的redis-py里scan_iter已经帮你封装好了游标循环,直接用更省心:

import redis r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True) for key in r.scan_iter(match='user:*', count=1000): print(key)

报错五:SCAN和HSCAN、SSCAN、ZSCAN搞混

SCAN扫的是整个 key 空间,HSCAN扫的是某个 hash 的 field,SSCAN扫 set 成员,ZSCAN扫 zset 成员。按前缀查 key 用SCAN,别用错。

6. 落地建议与工具链衔接

把SCAN用稳,记住三条:前缀查 key 一律走SCAN,KEYS在生产环境直接禁;COUNT按前缀稀疏度调,稀疏就往大调;遍历结束只看游标是否为 0,别被空返回吓到。

如果你在写这类运维脚本或封装工具类时需要快速验证一段逻辑、生成对照代码,可以用 TaoToken 的模型对话能力做辅助,入口在 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model-chat 。长期做编码和 Agent 类项目的话,Coding Plan 更适合持续调用,地址是 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan 。需要管理调用凭证就去 API Keys 页面 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keys ,接入细节看文档 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc 。

最后补一个实战小技巧:如果你的前缀查询是高频操作,与其每次SCAN全库,不如在写入时维护一个前缀索引 set,比如SET user:1的同时SADD idx:user user:1,查的时候直接SMEMBERS idx:user。代价是写入多一步、要处理过期同步,但查询从 O(N) 降到 O(1)。这个方案适合前缀种类固定、查询频繁的场景,SCAN则适合临时排查和低频迁移。两者不冲突,按场景选。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 19:24:22

做网站工商非法经营?3招避开风险与拖延最佳实践

做网站工商非法经营?3招避开风险与拖延最佳实践 改个需求建站公司拖一周,这是多少创业老板的噩梦?更让人头疼的是,为了赶进度或省钱,不少人直接在未备案、无许可证的情况下上线网站,结果被判定为 做网站工商非法经营…

作者头像 李华
网站建设 2026/9/27 19:24:11

山东学生做自我评价的网站,3步搞定性能优化避坑指南

山东学生做自我评价的网站,3步搞定性能优化避坑指南 找建站公司怕被坑高价,这大概是山东不少学生和家长最头疼的事。很多人觉得做个简单的自我评价网站,怎么报价就能过万,甚至被忽悠去买不需要的服务器套餐。其实,只要搞懂技术逻辑,自己就能把成本压下来,还能顺手搞定性能优化。…

作者头像 李华
网站建设 2026/9/27 19:24:08

宣传网站制作选哪家好

不会代码做宣传网站完整流程拆解 很多市场推广人员拿到“宣传网站制作”的任务,第一反应是头疼:我一行代码都不会写,这活儿怎么干?别慌,这恰恰是咱们非技术人员最容易踩坑、但也最容易通过标准化流程搞定的场景。你不需要成为程序员,只需要像个严谨的项目经理,把需求、资源、部署这三件事理顺。今天咱们就抛开那些虚…

作者头像 李华
网站建设 2026/9/27 19:22:26

2024能免费做网站避坑指南:5种方案费用拆解

2024能免费做网站避坑指南:5种方案费用拆解 找建站公司最怕什么?不是技术不行,而是报价单上的数字像滚雪球一样越滚越大。很多老板拿着5000块的预算去询价,回来发现对方张口就要3万,理由还是“配置高”“服务好”。这种信息差,就是咱们今天要撕开的口子。今天这份 能免费做网站 的 避坑指南…

作者头像 李华
网站建设 2026/9/27 19:22:09

告别域名服务器焦虑:单页网站在线生成速查手册

告别域名服务器焦虑:单页网站在线生成速查手册 域名解析报错、服务器配置超时、SSL证书部署失败……是不是每次搞单页网站,光搞懂这些基础设施就头大?别急着去啃那厚如砖头的运维文档,手里这本 速查手册 ,就是专门为你这种被技术细节劝退的站长准备的。…

作者头像 李华
网站建设 2026/9/27 19:22:08

中工信融做网站怎么样 性能优化避坑指南

中工信融做网站怎么样 性能优化避坑指南 找建站公司,最怕什么?不是功能做不全,而是花大钱买了个“电子垃圾”,加载慢、排名差,还没法退。很多老板在咨询中工信融这类服务商时,心里都在打鼓:这钱花得值不值?会不会被宰?其实,判断一家建站公司靠不靠谱,别听销售吹得天花乱坠,要看他们的技术底座是否扎实,尤其是…

作者头像 李华