Spring Cache 缓存更新与失效时机
Spring Cache 缓存更新与失效时机
Spring Cache 提供一层缓存抽象,可以用注解描述缓存读取和删除逻辑,底层由具体的 CacheManager 实现。它减少重复样板代码,但不会自动替应用解决缓存一致性、并发或 Redis 故障。
1. 读取缓存:@Cacheable
方法调用时,框架按缓存名和 Key 查找缓存。命中时可直接返回缓存值;未命中时执行方法,再按配置保存结果。Key 应能唯一表示方法参数或业务对象,避免不同租户或不同用户共享不该共享的结果。
2. 写入和删除:@CachePut、@CacheEvict
@CachePut总会执行目标方法,并把返回结果写入缓存,适合方法结果可直接作为缓存值的场景。@CacheEvict删除匹配的缓存项,适合数据库更新后让后续读取重新加载。allEntries = true会清空指定缓存区域中的所有条目,影响范围较大,使用前应评估。
以菜品分类缓存为例,查询分类列表时可按门店 ID 缓存;新增、修改或删除分类及菜品时,要失效受影响的缓存键。缓存粒度应与数据更新范围一致。
3. 事务边界会影响失效时机
如果缓存删除发生在数据库事务提交之前,并发读可能在删除后、数据库提交前读到旧数据并重新写进缓存。事务最终提交后,这份旧缓存仍可能存在。因此重要更新要考虑在数据库成功提交后再失效,或使用事务同步、可靠事件和补偿机制。
注解的具体时机还会受配置和框架版本影响;beforeInvocation 等选项有不同语义。不能只看注解名字就假设它一定在事务提交后执行。
4. 代理调用和自调用
Spring Cache 通常通过代理拦截方法调用。一个类内部直接调用自己的缓存方法,可能绕过代理,使注解没有被执行。可将缓存边界拆到另一个 Spring Bean,或通过明确的缓存 API 调用。
5. 缓存 Key 与数据隔离
为 Key 设计稳定命名,例如 dish:list:{shopId}:{categoryId}。检查:
- 是否包含影响结果的所有参数?
- 不同用户、门店或租户之间是否隔离?
- 删除数据时是否能定位对应缓存?
- Key 中是否包含不应暴露的个人信息?
6. 小结
Spring Cache 负责把缓存操作映射到方法调用;数据一致性策略仍需由业务设计。先画出读、写、事务提交和失效的时间线,再决定注解边界和缓存粒度。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 GuMing的殿堂!
评论





