GORM 本身不提供内置缓存,所谓“缓存插件”均为第三方实现且多依赖 Redis;本地内存缓存插件无法通过 db.Use() 安全加载,因其难以满足 GORM v2 回调时机、事务耦合、生命周期管理及缓存失效等核心要求。

GORM 本身不提供内置缓存,所谓“缓存插件”都是第三方实现,且绝大多数依赖 Redis —— 没有本地内存缓存插件能被 db.Use() 安全加载。
为什么不能直接用 db.Use() 注册本地缓存插件
你查到的很多“GORM 缓存插件”示例(比如基于 map 或 sync.Map 的简易实现)看似结构正确,但实际无法与 GORM 正常协同:它们通常在 Register() 方法里试图挂载 BeforeFind/AfterFind 回调,却忽略了 GORM v2 的 callback 执行时机与事务、会话生命周期强耦合。一旦开启事务,缓存逻辑可能读到未提交数据;若用 db.Session(&gorm.Session{NewDB: true}) 创建新会话,又会绕过缓存。更关键的是,db.Use() 要求插件必须实现完整生命周期管理,而本地缓存需要主动失效、驱逐、TTL 控制——这些都无法靠 callback 自动触发。
常见错误现象:
- 缓存命中后返回脏数据,尤其在并发更新+查询场景下
- 事务回滚了,但缓存已写入,导致“已回滚却查得到”
- 用
tx.First()查询时缓存未生效,因为 callback 没注册到事务会话上
gorm-redis-cache 是目前最接近生产可用的方案
它不是 GORM 官方插件,但通过包装 *gorm.DB 实现了透明缓存:所有 First、Find、Take 等查询方法被代理,在执行前先查 Redis,命中则跳过 DB;未命中则查库并写入缓存。它支持:
- 按 model + query conditions 自动生成 cache key(如
user:where:id=123) - 自动 TTL 设置(默认 5 分钟,可配)
- 支持结构体字段级缓存控制(用
gorm:"cache:false"跳过) - 写操作(
Create/Save/Delete)自动清除关联 key
但注意:它不处理跨服务缓存一致性。例如 A 服务更新了用户余额,B 服务仍可能从本地 Redis 读到旧值——这不是插件缺陷,而是分布式系统固有问题。
缓存一致性必须靠业务层兜底,不能依赖插件自动解决
Redis 缓存插件能帮你省掉“查缓存→查 DB→写缓存”的模板代码,但无法替代你对一致性的设计判断。真实项目中必须明确:
- 哪些数据允许短暂不一致(如商品描述)?可设长 TTL
- 哪些数据必须强一致(如账户余额)?应禁用缓存,或改用“先删缓存再更新 DB”策略
- 更新操作是否涉及多张表?缓存 key 是否覆盖全部关联路径?例如更新订单状态,是否也要清空该用户的“最近订单列表”缓存?
- 是否使用消息队列做缓存失效通知?若用,需确保消费端幂等,且延迟可接受
最容易被忽略的一点:缓存 key 的构造必须稳定。如果某次查询用了 db.Where("id = ?", id).First(&u),另一次用了 db.Where("id = ? AND status > 0", id).First(&u),插件会生成两个 key,但业务上它们本应互斥失效——这种场景必须手动控制,插件无能为力。











