go-redis v9是Fiber生态推荐的Redis客户端,支持连接池、自动重连、context取消等特性,需全局复用client、正确处理redis.Nil、显式调用.Result()、规范键名并避免KEYS命令。

用 go-redis 连 Redis,别碰 redigo
go-redis 是 Fiber 生态事实标准,redigo 虽底层但需手动管理连接、重试、超时,容易漏掉 Close() 导致 fd 耗尽。go-redis v9(当前最新稳定版)原生支持连接池、自动重连、pipeline 和 context 取消,直接对齐 Fiber 的 fiber.Ctx 生命周期。
安装命令:go get github.com/redis/go-redis/v9
常见错误:用 v8 或更早版本——v9 的 API 完全不兼容,比如 rdb.Get(ctx, key) 返回 *redis.StringCmd,不是 string;v8 是直接返回 string 或 error。
- 初始化必须传
context.Background()或带 timeout 的 ctx,不能传 nil - 连接字符串格式为
redis://[:password@]host:port[/db],密码含特殊字符要 url.PathEscape - 别在 handler 里每次 new
redis.NewClient(),全局复用一个 client 实例即可
配置连接池与健康检查
Fiber 启动时建立连接并验证可用性,比等第一个请求失败再报错更可控。go-redis 默认 max idle = 2,max active = 10,对中等流量够用;高并发建议调大:
opt := &redis.Options{
Addr: "localhost:6379",
Password: "", // no password
DB: 0,
PoolSize: 20,
MinIdleConns: 5,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
}
rdb := redis.NewClient(opt)
启动检查示例(放 main.go 初始化块):
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
if err := rdb.Ping(context.TODO()).Err(); err != nil {
log.Fatal("Redis connect failed:", err)
}
-
Ping()必须显式调.Err(),否则不会触发真实网络请求 - 别用
redis-cli ping替代代码检查——本地通不代表 Fiber 进程能通(Docker 网络、SELinux、防火墙都可能拦) - 生产环境加
MaxRetries: 3和RetryBackoff: 100 * time.Millisecond防瞬时抖动
在 Fiber 中间件或 Handler 里安全读写缓存
go-redis 所有命令都要求传 context.Context,Fiber 的 c.Context() 可直接透传,天然支持超时和取消:
app.Get("/user/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
key := "user:" + id
<pre class="brush:php;toolbar:false;">val, err := rdb.Get(c.Context(), key).Result()
if err == redis.Nil {
// 缓存未命中,查 DB,然后 set
user := fetchFromDB(id)
rdb.Set(c.Context(), key, user, 5*time.Minute).Err()
return c.JSON(user)
} else if err != nil {
return c.Status(500).SendString("cache error")
}
return c.SendString(val)})
- 必须区分
redis.Nil(key 不存在)和其它err(网络/序列化失败),直接if err != nil会把未命中当错误处理 - value 类型是 string,如果存的是 JSON,得自己
json.Unmarshal([]byte(val), &u);别依赖第三方序列化中间件,容易隐式 panic - 所有
rdb.Xxx()调用后必须接.Result()或.Val()才真正执行,否则只是构建命令对象
键命名与空值陷阱
Fiber 路由动态参数多,键名若不规范,极易冲突或无法清理。例如 /post/123?sort=desc 和 /post/123?sort=asc 应视为不同缓存项。
推荐格式:fmt.Sprintf("%s:%s:%s", appEnv, "post", hashQuery(c.Queries())),其中 hashQuery 对 map 按 key 排序后拼接成稳定字符串。
- 禁止用
keys user:*清理——O(n) 阻塞操作,线上严禁。改用scan:rdb.Scan(c.Context(), 0, "user:*", 100).Val() - 缓存空结果(如用户不存在)必须显式设为
"__EMPTY__"字符串,而不是跳过Set,否则反复穿透 DB - 避免在 key 中拼接时间戳或随机数——失去复用性;过期时间统一用
Set的expiration参数控制,别靠 key 命名
最易忽略的点:rdb.Get().Result() 返回的 val 是 string,但如果你之前 Set 的是 struct,没做 JSON 序列化,这里拿到的就是 Go 的默认字符串表示(类似 {Name:"foo"}),根本没法反解——序列化/反序列化必须由业务层严格配对。










