必须用 redis.NewClient() 初始化客户端,不可直接 new;需校验连通性、复用连接池、正确设置 TTL、安全构造 key、序列化 value、同步清理多级缓存。

用 redis.Client 连 Redis,别直接 new 一个结构体
Go 官方推荐的 github.com/go-redis/redis/v9 库里,redis.Client 是个接口,不能用 &redis.Client{} 或 new(redis.Client) 初始化——这会编译失败或 panic。必须用 redis.NewClient(),它内部做了连接池、命令路由、重试策略等封装。
- 初始化时传入
&redis.Options{Addr: "localhost:6379", Password: "", DB: 0},DB 值要和实际业务一致,不同服务混用 DB 容易串数据 - 务必调用
client.Ping(ctx).Err()检查连通性,否则第一次读写才报错,线上排查成本高 - 不要在 HTTP handler 里每次请求都
NewClient(),连接池复用是关键;全局单例 +sync.Once初始化更稳
SET 和 GET 之外,得用 GetEX 或 SetEX 控制过期
直接用 Set(ctx, key, value, 0)(TTL=0)等于永不过期,缓存雪崩风险拉满。Redis 本身不支持“不过期”,0 表示“不设置过期”,不是“过期时间为 0 秒”。真正想设 10 分钟过期,得用 SetEX(ctx, key, value, 10*time.Minute) 或 Set(ctx, key, value, 10*time.Minute)(v9 中 Set 的第 4 个参数就是 TTL)。
-
Get返回*redis.StringCmd,要用cmd.Val()取值,cmd.Err()判断是否 key 不存在(redis.Nil)还是真出错了 - 二级缓存场景下,如果 Redis 挂了,别让整个请求失败:检查
err == redis.Nil是缓存未命中,err != nil && err != redis.Nil才该降级查 DB - 避免用
SETNX+EXPIRE两步操作实现带过期的 set,竞态下可能 key 被设但没过期——SetNX不带 TTL,要用Set配合true第三个参数(即Set(ctx, k, v, ttl, redis.SetOptions{Mode: redis.SetModeNX}))
Web 请求中加缓存,别在 handler 里裸写 Get/Set
HTTP handler 里直接调 Redis,容易把缓存逻辑和业务逻辑搅在一起,后续加熔断、统计、日志都难。建议抽一层薄封装,比如 cache.Get(ctx, "user:"+uid),内部统一处理 context 超时、错误分类、打点。
- context 要带 timeout:
ctx, cancel := context.WithTimeout(r.Context(), 200*time.Millisecond),不然 Redis 卡住会拖垮整个请求链路 - 别用
fmt.Sprintf("user:%s:profile", uid)拼 key,Key 冲突或注入风险大;固定前缀 +strconv转义或url.PathEscape更安全 - value 存 struct?先
json.Marshal再存,别直接传 struct 指针给Set——序列化失败会静默丢数据,且 Redis 只认字节流
本地内存缓存 + Redis,两级失效难同步?用 Del 主动踢掉本地缓存
二级缓存(如 bigcache + Redis)常见问题是:DB 更新后,只删了 Redis key,本地缓存还留着脏数据。没法靠 TTL 自动对齐,因为本地缓存通常没自动刷新机制。
立即学习“go语言免费学习笔记(深入)”;
- 更新 DB 后,必须同步执行
redisClient.Del(ctx, "user:123")和localCache.Delete("user:123"),顺序不能反——先删远端再删本地,防止删完本地、还没删 Redis 时有新请求把旧值又刷进本地 - 如果本地缓存用了 LRU(如
lru.Cache),注意它的Delete是线程安全的,但Get不是,得自己加sync.RWMutex包一层 - 别指望用 Redis 的
Pub/Sub做二级缓存失效通知——网络分区、客户端断连、消息丢失都会导致本地缓存长期不一致,主动删除更可控
二级缓存最难的从来不是怎么存,而是失效时机和范围——DB 改一条记录,到底该删几个 key、哪些服务要删、删漏了怎么兜底,这些比连上 Redis 复杂得多。










