Redis 模式匹配:KEYS vs SCAN 及 Go 实战示例
【摘要】 在使用 Redis 时,我们经常需要按某种命名规则查找一批 key,比如:user:1001:profileuser:1002:profilesession:abc123session:def456此时就需要用到 Redis 的**模式匹配(Pattern Matching)**功能。Redis 提供了两个关键命令:KEYS 和 SCAN。它们都能匹配 key,但使用场景和风险差异巨大。 ?...
在使用 Redis 时,我们经常需要按某种命名规则查找一批 key,比如:
user:1001:profileuser:1002:profilesession:abc123session:def456
此时就需要用到 Redis 的**模式匹配(Pattern Matching)**功能。Redis 提供了两个关键命令:KEYS 和 SCAN。它们都能匹配 key,但使用场景和风险差异巨大。
🔍 1. 通配符匹配规则
Redis 支持以下通配符:
| 模式 | 含义 | 示例匹配(pattern = "user:*") |
|---|---|---|
* |
匹配任意数量字符(含 0) | user:1, user:100:info, user: |
? |
匹配单个字符 | user:a? → user:a1, user:ab ✅;user:abc ❌ |
[abc] |
匹配括号中任一字符 | log:[123] → log:1, log:2 ✅ |
[a-z] |
匹配区间内任一字符 | temp:[a-c] → temp:a, temp:c ✅ |
💡 实际开发建议:统一 key 命名规范,比如
namespace:id:field,让匹配更可靠。
⚠️ 2. KEYS 命令:快但危险!
KEYS user:*
- ✅ 简单直接:一次性返回所有匹配 key
- ❌ 阻塞 Redis 单线程!大数据量时可能导致服务卡顿甚至超时
- 🚫 严禁在生产环境使用(除非你 100% 确认 key 总数极少)
✅ Go 示例(仅限开发/调试)
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
keys, err := rdb.Keys(ctx, "user:*").Result()
if err != nil {
panic(err)
}
fmt.Println("Found keys:", keys)
// Output: [user:1001:profile user:1002:profile]
}
📌 注意:
Keys()是KEYS命令的封装,切勿用于线上。
✅ 3. SCAN 命令:安全、分批、推荐!
SCAN 0 MATCH user:* COUNT 10
→ 返回 [cursor, [key1, key2, ...]]
- ✅ 非阻塞:每次只查一小批,不影响线上服务
- ✅ 游标迭代:直到 cursor 回到
0表示结束 - ✅ 完美适配生产环境大规模 key 遍历
Go 实战:安全遍历所有匹配 key
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func scanKeys(ctx context.Context, rdb *redis.Client, pattern string, count int64) ([]string, error) {
var keys []string
var cursor uint64 = 0
for {
var err error
var scanned []string
scanned, cursor, err = rdb.Scan(ctx, cursor, pattern, count).Result()
if err != nil {
return nil, err
}
keys = append(keys, scanned...)
// cursor == 0 表示迭代结束
if cursor == 0 {
break
}
}
return keys, nil
}
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
keys, err := scanKeys(ctx, rdb, "user:*", 10) // 每次最多查 10 个
if err != nil {
panic(err)
}
fmt.Println("Scanned keys:", keys)
// Output: [user:1001:profile user:1002:profile]
}
⚙️ 参数说明:
cursor: 初始为0,后续用上一次返回的cursorpattern:"user:*","session:*"等count: 建议值(非精确),如10/100。太小效率低,太大影响性能。
✅ 即使 Redis 有百万 key,
SCAN也能零感知完成遍历。
🛠️ 4. 最佳实践总结
| 项目 | 建议 |
|---|---|
| ✅ 生产环境 | 只用 SCAN,禁用 KEYS |
| ✅ 命名规范 | 使用层级命名:{type}:{id}:{field}(如 order:20251203001:status) |
| ✅ 性能调优 | COUNT 初始可设 100;若响应慢,调低至 10~50 |
| ✅ 防误用 | 代码审查时 grep "KEYS",或通过 Redis 配置禁用:rename-command KEYS "" |
🔚 结语
- 想快速查几个 key?👉
KEYS(仅本地/测试) - 想安全遍历海量 key?👉
SCAN+ Go 循环(生产唯一选择)
掌握这个模式,能帮你避免 Redis 线上事故,写出更健壮的缓存层代码!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)