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

简介: Redis支持通配符匹配(*、?、[])查找key,但`KEYS`命令会阻塞服务,严禁用于生产;推荐使用非阻塞的`SCAN`命令分批遍历,配合规范命名(如`user:1001:profile`)和合理COUNT值,确保线上稳定。

在使用 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 线上事故,写出更健壮的缓存层代码!


相关文章
|
2月前
|
安全 Java API
SpringBoot 4 黑科技:接口组 ——10 行代码管理 100+ API 客户端
Spring 7 新增「HTTP接口组」特性,告别重复`@Bean`声明与手动配置。通过`@ImportHttpServices`按业务分组(如github、stackoverflow),支持统一超时、Token、baseUrl等配置,Java代码+YAML双驱动,大幅降低配置冗余,提升可维护性与开发效率。(239字)
|
2月前
|
前端开发 Java API
Python MyBoot入门:像写SpringBoot 一样写python
MyBoot是Python版Spring Boot,主打“约定优于配置”,支持自动装配、依赖注入与类Spring注解(如@RestController/@service)。内置HTTP/2、Swagger、健康检查等,单文件启动,30秒初始化项目,零样板配置,专为快速开发企业级API而生。
|
2月前
|
安全 IDE Java
IDEA 2025.3新特性: 让 Java 空安全落地更丝滑
JSpecify 1.0正式落地,Spring Boot 4、JUnit 6等已默认支持!本文详解IDEA 2025.3如何与NullAway协同实现真正一致的空安全:智能降噪、统一suppress、平滑迁移方案一应俱全——空安全,从此不止于注解。
|
2月前
|
人工智能 IDE Go
GoLand 2025.3 正式发布:Claude Agent 深度集成!
GoLand 2025.3 正式发布!新增实时资源泄漏检测、开箱即用Terraform支持、Junie×Claude双AI Agent协同、K8s全流程集成、无项目模式秒开.go文件、golangci-lint fmt深度整合,并启用护眼Islands默认主题,全面升级云原生开发体验。(239字)
|
2月前
|
人工智能 缓存 Java
Spring AI 1.1 新特性详解:五大核心升级全面提升AI应用开发体验
Spring AI 1.1正式发布!新增Model Context Protocol(注解式工具注册)、Prompt缓存(降本90%)、递归顾问(自修正推理)、Google GenAI/ElevenLabs语音支持,及推理模式(输出思考步骤),全面提升AI应用开发效率与体验。(239字)
|
2月前
|
安全 Go API
Go1.26新提案:errors.AsType —— 更安全、更简洁的错误类型检查方案
Go 1.26 新增 `errors.AsType[E error](err error) (E, bool)`,以泛型替代反射实现错误类型匹配。相比传统 `errors.As`,它无需预声明变量、避免指针误用、杜绝运行时 panic,支持 `if x, ok := AsType[T](err); ok` 短声明,作用域更安全,性能更高,代码更简洁清晰。(239字)
|
2月前
|
安全 中间件 Go
Go 语言三大进阶函数技巧
Go函数进阶指南:3个必学技巧——①变长参数(...T)灵活处理任意数量参数;②函数作为一等公民,支持回调与策略模式;③闭包捕获变量,实现状态记忆与配置化。提升代码复用性、安全性和专业度!
|
2月前
|
程序员 Go 数据库
Go线上事故复盘:一个 (bool, error) 引发的误判,差点让脏数据入库
Go中「碎裂失败」陷阱:用`bool, error`双返回值表达成败,导致4种歧义状态(如`false, nil`含义模糊),违背“非法状态不可表示”原则。正解是统一由`error`判定成败,并通过哨兵错误或自定义类型封装失败原因——一块表,才知准点。
|
2月前
|
Java Go
Go 里没有 override,但有更清爽的替代方案!
小明学Java后转Go,发现Husky嵌入Dog却无法“重写”bark方法?别急!Go不支持继承式override,但用**接口定义行为 + 结构体嵌入复用 + Functional Options动态定制**,三步轻松实现更灵活、低耦合的“伪override”。清爽、显式、真Go风!
|
2月前
|
Go
Go 的 nil 接口:你眼中的 `nil`,Go 眼里的“带户口的空房间”
Go接口非空之谜:`nil`指针赋值给接口后,因接口含“类型+数据”双字段,仅数据为`nil`而类型已注册,故接口整体不为`nil`!三招避坑:①返回裸`nil`;②类型断言后判空;③用`reflect`通用检测。真相:接口是带户口本的空房,有户即“有人”。