Redis 集成:go-redis 客户端、缓存读写、连接池参数
Redis 常用来做缓存、分布式限流与登录态。go-redis 是社区最主流的客户端,v9 需要 Go 1.18+,包路径为 github.com/redis/go-redis/v9,API 以官方仓库 README 为准。
1. 安装与连接
import (
"context"
"time"
"github.com/redis/go-redis/v9"
)
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "", // 无密码留空
DB: 0,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
ctx := context.Background()
if err := rdb.Ping(ctx).Err(); err != nil {
log.Fatalf("ping redis: %v", err)
}
defer rdb.Close()
v9 起所有命令都要显式传 context.Context,这是与旧版最大的区别。
2. 缓存读写
核心套路:先查 Redis,命中直接返回;未命中回源数据库再回填并设置过期时间:
func GetUser(ctx context.Context, id int) (*User, error) {
val, err := rdb.Get(ctx, "user:"+strconv.Itoa(id)).Result()
if err == nil { // 命中
var u User
json.Unmarshal([]byte(val), &u)
return &u, nil
}
if !errors.Is(err, redis.Nil) { // 非“键不存在”的真实错误
return nil, err
}
u, err := findUserByID(ctx, id) // 未命中:回源数据库
if err != nil {
return nil, err
}
data, _ := json.Marshal(u)
rdb.Set(ctx, "user:"+strconv.Itoa(id), data, 5*time.Minute)
return u, nil
}
键不存在的错误是哨兵值 redis.Nil,必须用 errors.Is(err, redis.Nil) 判断,不能把其他网络错误当“未命中”处理。
3. 连接池参数
redis.Options 里的池参数建议按负载调整,默认值随版本略有差异,以官方文档为准:
PoolSize:单实例最大连接数;MinIdleConns:常驻空闲连接,压延迟建议设 8 以上;ConnMaxIdleTime:空闲连接回收时间;ConnMaxLifetime:连接最大存活时间,建议设置以轮换底层 TCP;DialTimeout / ReadTimeout / WriteTimeout:建连与读写超时。
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
PoolSize: 50,
MinIdleConns: 10,
ConnMaxIdleTime: 30 * time.Minute,
ConnMaxLifetime: time.Hour,
})
4. 在 Gin 里使用
把 *redis.Client 做成全局单例或注入到 handler,调用时复用请求上下文,请求结束连接自动归还连接池,无需手动管理:
val, err := rdb.Get(c.Request.Context(), key).Result()
注意点
- 每次请求都
redis.NewClient是错误用法,客户端应全局单例、程序退出时才 Close。 - 缓存 value 建议存 JSON 字符串或二进制,别把结构体直接塞进去。
- 更新数据后记得删/刷新相关缓存键,防止脏读。
- 缓存穿透/雪崩的缓解(空值缓存、随机过期)属架构话题,本章不展开。
小结
go-redis v9 的统一签名是「命令(ctx, 参数...).Result()/Err()」。记住三点:redis.Nil 判空、连接池集中配置、客户端全局复用,缓存层就能平稳跑起来。命令全集与选项默认值见官方 README 与 pkg.go.dev。