gcache 与 Redis 缓存的选型与配合
结论:gcache 是进程内缓存(默认内存 Adapter、单例 g.Cache()),快但各实例不共享;Redis 是分布式共享缓存;常见做法是“gcache 一级 + Redis 二级 + DB 兜底”的旁路缓存,并可配合 ORM 查询缓存使用。
一、选型对比
| 维度 | gcache | Redis |
|---|---|---|
| 存储位置 | 进程内存 | 独立服务 |
| 访问速度 | 最快 | 快(有网络开销) |
| 共享性 | 单实例内 | 多实例共享 |
| 一致性 | 实例间不一致 | 全局一致 |
| 适用 | 单机热点、进程内状态 | 分布式共享、锁、计数等 |
| 容量/持久化 | 进程重启即失 | 可配持久化、容量大 |
二、配合套路(旁路缓存)
- 读:先查 gcache(L1)→ 未命中查 Redis(L2)→ 未命中查 DB 并回填两级。
- 写:先更新 DB,再删/失效缓存(推荐“删缓存”降低并发不一致窗口)。
- TTL 加随机抖动防雪崩;热点键注意穿透问题。
代码示例
func GetUser(ctx context.Context, id int64) (*User, error) {
key := fmt.Sprintf("user:%d", id)
// L1 进程内
if v := g.Cache().MustGet(ctx, key); !v.IsNil() {
var u User
_ = v.Struct(&u)
return &u, nil
}
// L2 分布式缓存(示意,可走 g.Redis().Do GET/SET)
// L3 DB
var u User
if err := dao.User.Ctx(ctx).WherePri(id).Scan(&u); err != nil {
return nil, err
}
_ = g.Cache().Set(ctx, key, u, 5*time.Minute)
return &u, nil
}
// ORM 查询缓存(默认基于 gcache,可配置 Redis Adapter)
var users []User
err := dao.User.Ctx(ctx).Cache(time.Minute, "user:list").Scan(&users)
常见追问 / 记忆点
- 追问:多级缓存一致性如何保证?→ 一般只追求最终一致:短 TTL + 更新后删缓存 + 必要时失效广播(以官方文档/中间件方案为准)。
- 追问:缓存更新与删除选哪个?→ 写多场景优先删缓存,避免并发写旧值覆盖。
- 记忆点:一级内存二级 Redis 三级 DB;“先更库、后删缓存、TTL 加抖动”。