最轻量且生产可用的方案是@Cacheable(sync = true)配合RedissonSpringCacheManager,但必须确保其生效,否则sync参数无效;原生RedisCacheManager完全忽略sync,需引入redisson-spring-boot-starter并验证日志中RedissonSpringCacheManager初始化成功。

@Cacheable(sync = true) + Redisson 是最轻量且生产可用的方案,但必须确认 RedissonSpringCacheManager 已生效,否则 sync 参数完全不触发锁逻辑。
为什么 @Cacheable(sync = true) 看似简单却常失效
它不是“加个参数就自动防击穿”,而是依赖完整的 Redisson 缓存管理器替换。Spring Boot 原生的 RedisCacheManager 对 sync = true 完全忽略——你写了等于没写。
- 必须显式引入
redisson-spring-boot-starter(推荐 3.27.2+),不能只靠spring-boot-starter-data-redis - 检查日志是否出现
RedissonSpringCacheManager初始化成功,而不是RedisCacheManager -
@Cacheable(value = "user", key = "#id", sync = true)中的value必须与缓存配置里定义的 cache name 严格一致,否则 fallback 到默认 manager
锁超时和业务耗时不匹配会直接导致防护失效
Redisson 的看门狗默认每 30 秒续期一次,但如果数据库查询 P99 耗时达到 35 秒,锁可能在写缓存前就被释放,第二个线程趁虚而入。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 务必监控真实场景下
getById类方法的 P99 响应时间,用spring.redis.redisson.config配置项调大lockWatchdogTimeout - 不要把锁超时设得比 DB 查询最大预期耗时还短;例如 DB 平均 200ms、P99 800ms,锁超时至少设为 1200ms
-
sync = true只作用于@Cacheable,@CachePut和@CacheEvict不参与锁机制,更新逻辑需单独加锁
降级不是“Redis挂了就切本地”,而是全过程可控
当 Redis 连接失败时,@Cacheable 会直接抛 RedisConnectionFailureException,整个请求失败——它没有 fallback 路径。
- 真正可用的降级必须放弃
@Cacheable,改用RedisTemplate显式操作,并在外层try-catch捕获DataAccessException子类 - 本地缓存必须用
Caffeine,而非ConcurrentHashMap:前者支持expireAfterWrite(5, TimeUnit.MINUTES),避免脏数据滞留 - Redis 恢复后,Caffeine 的
refreshAfterWrite可自动拉取新值;ConcurrentHashMap 得自己写定时任务轮询,极易漏掉或重复
最难的不是写出 setIfAbsent 或配好 sync = true,而是让锁生命周期、空值 TTL、本地缓存过期、Redis 连接池超时这四组时间参数在压测中不互相撕扯——少一个埋点、缺一条监控,上线后问题就藏在并发毛刺里出不来。

















