@cacheevict主动失效适用于写多读多且不容脏数据的场景,如订单状态更新、库存扣减,需配合@cacheable使用,慎用beforeinvocation=true,避免删缓存后数据库更新失败导致数据丢失。

缓存失效策略不是“选一个就好”,而是得看数据变不变、谁在读、出不出得起错——选错会直接导致数据库被打爆或用户看到旧数据。
@CacheEvict 主动失效适合什么场景
主动失效本质是“数据一改,立刻删缓存”,适用于写多、读也多、且不能容忍脏数据的业务,比如订单状态更新、库存扣减。
- 必须配合
@Cacheable使用,否则删完没重建,下次查就是空 -
beforeInvocation = true要慎用:如果数据库更新失败,缓存却删了,就会造成短暂数据丢失 - 高频写入时,
allEntries = true会清空整个缓存名(如"orders"),容易引发雪崩,应尽量用精准key - 删除失败不抛异常,默认静默,建议加日志或重试逻辑,例如:
cache.delete("user:" + id)后检查返回值
定时过期(TTL)为什么经常出问题
单纯靠 Redis 的 EXPIRE 或 Spring 的 entryTtl 设置固定过期时间,最容易触发缓存雪崩——尤其当大量 key 共享同一基础 TTL 时。
- Spring Boot 默认全局 TTL(如
time-to-live: 3600000)对所有缓存生效,但不同业务数据变更频率差异大,硬套会出事 - Redis 的过期是惰性 + 定期删除,
KEYS *查不到已过期但未被扫描到的 key,TTL返回-1或-2也不直观 - 解决办法不是“全设长一点”,而是给每个缓存名配独立 TTL,并叠加随机偏移,例如:
Duration.ofSeconds(300 + ThreadLocalRandom.current().nextInt(60)) - 配置上优先用
getRedisCacheConfigurationMap()显式指定各 cache 名的 TTL,别依赖全局默认值
缓存击穿和穿透怎么绕不开 @CacheEvict
击穿和穿透不是“要不要删缓存”的问题,而是“删了之后怎么防并发重建”和“空结果要不要缓存”的问题,@CacheEvict 只负责删,后续逻辑得自己补。
- 热点 key 过期瞬间被并发访问,
@CacheEvict不管这事——得配合分布式锁(如 Redis 的SET key val NX PX 5000)或本地锁控制重建入口 - 查不到数据时,
@Cacheable(unless = "#result == null")会让空结果不进缓存,加剧穿透;正确做法是缓存null值并设短 TTL(如 2 分钟) -
@CacheEvict删除后,下一次@Cacheable查询若仍为空,需确保该空值也被缓存,否则每次都在穿透 - 布隆过滤器这类前置校验,和
@CacheEvict是正交的——它拦非法 key,@CacheEvict清合法 key,两者要一起上
真正难的不是选策略,而是把“删缓存”这个动作嵌进业务流程里:删早了,数据库还没写成功;删晚了,中间窗口出现脏数据;删失败了,没人知道。这些细节不会报错,但会在凌晨三点让你收到告警。











