Spring进阶特性 缓存抽象(@Cacheable)

Spring 缓存抽象(@Cacheable)是真正把系统性能从“能用”提升到“飞起”的杀手级进阶特性。
大厂面试必问、性能调优必备、99% 项目必开。
以下是 2025 年最新、最地道的 Spring Boot 3.x(Spring 6 + JDK 17+)缓存全套实战指南,直接抄到生产零事故。

1. 2025 年主流缓存方案对比(必背表)

缓存方案是否支持分布式TTL/空值防穿透淘汰策略监控/热key分析推荐场景
Caffeine单机支持W-TinyLFU极强99% 项目首选(本地缓存之王)
Redis(原生 + Redisson)分布式支持8种分布式缓存、大数据量、跨服务共享
Caffeine + Redis 二级缓存分布式支持双层淘汰极强高并发核心业务(电商、秒杀、订单)
Ehcache 3.x单机支持LFU/LRU一般传统项目、配置复杂场景
JetBrains XCache(新兴)单机支持更优W-TinyLFU2025 新贵,性能略胜 Caffeine

2025 年真实结论:
单机项目直接上 Caffeine,分布式项目一律 Caffeine + Redis 二级缓存!

2. 生产级配置(直接复制粘贴)

方案一:纯 Caffeine(推荐 90% 项目)

# application.yml
spring:
  cache:
    type: caffeine
    cache-names: userCache,menuCache,dictCache,hotGoodsCache   # 预创建
    caffeine:
      spec: initialCapacity=100,maximumSize=500,expireAfterWrite=600s,recordStats=true
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            // 全局10分钟
            .recordStats());                                      // 开启统计,暴露到/actuator/prometheus

        // 不同缓存不同策略
        cacheManager.setCacheSpecification("hotGoodsCache,expireAfterWrite=30s,maximumSize=100");
        cacheManager.setCacheSpecification("userCache,expireAfterAccess=30m");
        return cacheManager;
    }
}

方案二:Caffeine + Redis 二级缓存(大厂标配)

spring:
  cache:
    type: redis
    redis:
      time-to-live: 600000          # 默认10分钟
      cache-null-values: true       # 防止缓存穿透!必开
      use-key-prefix: true
      key-prefix: "cache:"
@Configuration
@EnableCaching
public class TwoLevelCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 一级 Caffeine
        Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
            .expireAfterWrite(30, TimeUnit.SECONDS)   // L1 30秒
            .initialCapacity(100)
            .maximumSize(500);

        // 二级 Redis
        RedisSerializationContext.SerializationPair<Object> pair =
            RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());

        RedisCacheConfiguration redisConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10))              // L2 10分钟
            .serializeValuesWith(pair)
            .prefixCacheNameWith("cache:")
            .disableCachingNullValues(false);              // 允许缓存null

        return new CaffeineRedisCacheManager(
            CaffeineRedisCacheManager.builder(factory)
                .caffeine(caffeine)
                .initialCacheNames(Set.of("userCache", "hotGoods"))
                .withCacheConfiguration("userCache", redisConfig)
        );
    }
}

社区最成熟的二级缓存实现(强烈推荐):
https://github.com/langhsu/mcaffeine (已支持 Spring Boot 3)

3. 八种注解全家桶正确用法(重点!)

@Service
@RequiredArgsConstructor
public class UserService {

    // 1. 基本用法(最常用)
    @Cacheable(cacheNames = "userCache", key = "#id")
    public UserDTO getById(Long id) {
        return userMapper.selectById(id);
    }

    // 2. 条件缓存(id > 0 才缓存)
    @Cacheable(cacheNames = "userCache", key = "#id", condition = "#id > 0")
    public UserDTO getById(Long id) { ... }

    // 3. 防缓存穿透(结果为null也缓存5分钟)
    @Cacheable(cacheNames = "userCache", key = "#id", cacheNullValues = true, ttl = 300000)
    public UserDTO getById(Long id) { ... }

    // 4. 更新缓存(新增/修改后调用)
    @CachePut(cacheNames = "userCache", key = "#user.id")
    public UserDTO update(UserDTO user) {
        userMapper.updateById(user);
        return user;                              // 注意:必须返回!否则不更新缓存
    }

    // 5. 清除缓存(删除、状态变更)
    @CacheEvict(cacheNames = "userCache", key = "#id")
    public void delete(Long id) {
        userMapper.deleteById(id);
    }

    // 6. 清空整个缓存区(慎用!)
    @CacheEvict(cacheNames = "menuCache", allEntries = true)
    public void refreshAllMenu() { ... }

    // 7. 组合注解(超级实用!)
    @Caching(
        put = {@CachePut(cacheNames = "userCache", key = "#user.id")},
        evict = {
            @CacheEvict(cacheNames = "userCache", key = "#user.phone"),
            @CacheEvict(cacheNames = "userListCache", allEntries = true)
        }
    )
    public UserDTO updateWithMultiCache(UserDTO user) { ... }

    // 8. 自定义key生成器(推荐!统一管理)
    @Cacheable(cacheNames = "userCache", keyGenerator = "wiselyKeyGenerator")
    public UserDTO getByPhone(String phone) { ... }
}

自定义 KeyGenerator(杜绝 key 撞车):

@Component("wiselyKeyGenerator")
public class WiselyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return target.getClass().getSimpleName() + "." + method.getName()
               + "(" + StringUtils.arrayToDelimitedString(params, "_") + ")";
    }
}

4. 生产级进阶技巧(大厂真题)

  1. 缓存预热(启动时加载热点数据)
@PostConstruct
public void preheat() {
    executor.submit(() -> {
        List<Long> hotUserIds = Arrays.asList(1L, 2L, 3L);
        hotUserIds.forEach(this::getById);
    });
}
  1. 缓存击穿解决方案(热点key)
@Cacheable(cacheNames = "hotGoods", key = "#goodsId", sync = true)  // 重点:sync=true 加本地锁!
public GoodsDTO getHotGoods(Long goodsId) {
    return queryFromDb(goodsId);
}
  1. 缓存雪崩解决方案
  • 不同缓存设置随机过期时间:expireAfterWrite=600s,expireAfterWriteRandom=120s
  • 二级缓存 + Caffeine 兜底
  • 降级策略
  1. 监控必接(否则你永远不知道缓存效果)
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,caches
  metrics:
    enable:
      caffeine: true

访问 /actuator/caches/actuator/metrics/caffeine.cache.hit.rate

5. 终极推荐架构(2025 真实生产)

项目规模推荐方案
小中型项目纯 Caffeine(配置简单,性能拉满)
中大型单体/微服务Caffeine + Redis 二级缓存(mcaffeine)
高并发核心系统(订单、商品)Caffeine(L1 50ms) + Redis(L2 10min) + 热点本地锁 + 预热 + 监控
传统金融/政府项目Redis 单层 + 分布式锁 + 严格一致性要求

需要我直接给你一个开箱即用的生产级缓存模板项目吗?包含:

  • Caffeine + Redis 二级缓存(mcaffeine)
  • 统一缓存Key规范 + 自动生成
  • 缓存预热 + 热点保护
  • 完整监控大盘(Grafana模板)
  • 缓存异常降级兜底

说一声,我直接发你 GitHub 地址,拿来就能跑生产。

文章已创建 3070

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部