Redis 模式匹配:KEYS vs SCAN 及 Go 实战示例

举报
golang学习记 发表于 2026/02/22 11:46:43 2026/02/22
【摘要】 在使用 Redis 时,我们经常需要按某种命名规则查找一批 key,比如:user:1001:profileuser:1002:profilesession:abc123session:def456此时就需要用到 Redis 的**模式匹配(Pattern Matching)**功能。Redis 提供了两个关键命令:KEYS 和 SCAN。它们都能匹配 key,但使用场景和风险差异巨大。 ?...

在使用 Redis 时,我们经常需要按某种命名规则查找一批 key,比如:

  • user:1001:profile
  • user:1002:profile
  • session:abc123
  • session:def456

此时就需要用到 Redis 的**模式匹配(Pattern Matching)**功能。Redis 提供了两个关键命令:KEYSSCAN。它们都能匹配 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,后续用上一次返回的 cursor
  • pattern: "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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。