Redis更稳妥;高频读、低频写、允许短暂不一致时选Redis,单机低QPS且需强一致性时可用gocache等本地缓存,避免盲目引入Redis增加网络开销。

缓存该选 redis 还是 memory?看查询频率和一致性要求
高频读、低频写、允许短暂不一致的接口,redis 是更稳妥的选择;如果只是单机部署、QPS 在 5000 以内、且数据更新后必须立刻失效,用 gocache 或 groupcache 这类内存缓存更轻量。别一上来就上 redis——本地缓存没压测就加 redis,反而因网络往返拖慢响应。
常见错误:把 time.Now().Unix() 当作缓存 key 的一部分,导致每次请求 key 都不同,缓存完全失效。
- key 应该只包含查询参数的确定性哈希,比如
fmt.Sprintf("user:profile:%d", userID) - 避免在 key 中拼接时间戳、随机数、请求 ID 等非幂等字段
- 若需区分用户权限级别,可加入角色标识,但必须确保其稳定(如
"user:profile:%d:admin")
gin.Context 里怎么安全塞进缓存结果?别直接改 c.Keys
很多人习惯往 c.Keys 里塞临时数据,但这不是缓存层该做的事——它不提供过期、淘汰、并发安全机制。真正该做的是在 handler 里显式调用缓存客户端,并控制生命周期。
示例场景:查用户信息接口,先查 redis,命中则反序列化返回;未命中则查 DB,写入缓存再返回:
// 注意:err 检查不能省,redis.ErrNil 和 redis.Nil 是两回事
val, err := rdb.Get(ctx, key).Result()
if err == redis.Nil {
// 缓存未命中,查 DB
user, err := db.GetUserByID(ctx, userID)
if err != nil {
c.JSON(500, gin.H{"error": "db query failed"})
return
}
// 写缓存,设置 TTL(别用永不过期!)
rdb.Set(ctx, key, json.Marshal(user), 5*time.Minute)
c.JSON(200, user)
} else if err != nil {
// redis 连接异常,降级走 DB(别 panic!)
user, _ := db.GetUserByID(ctx, userID)
c.JSON(200, user)
} else {
// 缓存命中,反序列化返回
var user User
json.Unmarshal([]byte(val), &user)
c.JSON(200, user)
}
缓存穿透、雪崩、击穿——不是理论,是上线当天就会遇到的问题
这三个词不是面试话术,而是你第一个缓存接口上线后第 3 小时可能触发的线上故障。
-
穿透:恶意或错误请求查不存在的
userID=999999999,DB 被打爆。解法:缓存空值(""或特殊标记),并设较短 TTL(如 2 分钟) -
雪崩:大量 key 同一时刻过期,请求全打到 DB。解法:TTL 加随机偏移,比如
5*time.Minute + time.Duration(rand.Int63n(60))*time.Second -
击穿:热点 key(如首页 banner)过期瞬间被万级并发重查 DB。解法:用
redis SETNX+ 本地锁(或直接用redlock),只放一个请求回源,其余等待
别指望靠“加机器”扛住击穿——锁粒度要细到 key 级,且必须有超时退出,否则死锁会卡死整个服务。
为什么你的缓存接口压测 QPS 上不去?检查 context.WithTimeout 和连接池
缓存本身很快,但慢在超时设置不合理或连接池耗尽。Gin 默认不带 context timeout,而 redis.Client 的 WithContext 方法依赖你传入的 ctx 是否带 deadline。
- 每个 handler 开头务必用
ctx, cancel := context.WithTimeout(c.Request.Context(), 200*time.Millisecond) -
redis.NewClient必须配置PoolSize(默认 10,小项目够用,高并发至少设为 50+) - 别在 handler 里 new redis client——复用全局实例,否则 fd 泄漏、连接数爆炸
- DB 查询也得套同样 timeout,否则缓存未命中时 DB 慢查询拖垮整个接口
缓存不是银弹。最常被忽略的一点:缓存更新策略。写操作后是删缓存还是更新缓存?删更安全,但要注意「删缓存 → 写 DB」中间若有失败,会导致脏数据;更新缓存看似简单,但多实例部署时,各节点缓存更新顺序不可控,容易不一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











