Spring Cache修仙指南:从青铜到王者的缓存通关秘籍
2025-04-23 08:39 阅读(46)

一、缓存世界的生存法则

1.1 缓存是什么?程序员的"小卖部哲学"

想象你是个996的社畜程序员,每次想喝肥宅快乐水都要跑到3公里外的仓库(数据库)去拿,直到有一天...

你在工位旁开了个小卖部(缓存)!


高频访问:肥宅水、泡面常备(热点数据)

快速响应:伸手就能拿到(内存级速度)

过期策略:薯片每周五补货(TTL时间)


这就是Spring Cache的奥义——让数据库这个"大冤种"少跑腿!

1.2 三大护法注解(缓存界的桃园三结义)

注解江湖绝技口头禅
@Cacheable乾坤大挪移(优先取缓存)"这题我见过,直接抄答案!"
@CachePut移花接木(强制更新缓存)"旧的不去新的不来!"
@CacheEvict 化骨绵掌(删除缓存) "毁灭吧,赶紧的!"

二、Redis合体大法(本地缓存VS分布式)

2.1 单机模式:小卖部的自我修养

// 配置本地缓存(Caffeine)
spring.cache.type=caffeine
spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=5m

就像在工位抽屉里藏零食,优点是伸手就能摸到,缺点是换工位(重启服务)就没了...


2.2 集群模式:开连锁超市

spring:
  cache:
    type: redis
    redis:
      time-to-live: 30m  # 商品保质期
      key-prefix: "BUFF:" # 货架标签

现在你是连锁超市老板(Redis集群),全公司程序猿都能来蹭吃蹭喝,但记得:


别卖过期食品(设置TTL)

做好防盗措施(密码配置)

别把货架堆太满(内存限制)

三、注解实战:缓存界的"摸鱼"技巧

3.1 @Cacheable的正确躺姿

@Cacheable(value = "外卖", 
           key = "#userId + '_' + #restaurantId",
           unless = "#result.price > 50") // 超过50块的不缓存
public String 点外卖(Long userId, String restaurantId) {
   System.out.println("【真实伤害】数据库查询中...");
   return "香辣鸡腿堡套餐";
}

当同事问你为什么总不写SQL——"因为我在摸鱼啊(缓存命中)!"


3.2 缓存清理的十八种姿势

@CacheEvict(value = "购物车", 
            allEntries = true, // 清空整个货架
            beforeInvocation = true) // 双十一前先清缓存
public void 结算购物车(Long userId) {
   // 支付成功后让缓存原地爆炸
}

记住:删缓存要像分手一样干脆,别给脏数据留机会!


四、防坑指南:缓存世界的黑暗森林

4.1 缓存穿透:防羊毛党攻略

@Cacheable(value = "商品详情", 
           unless = "#result == null") // 缓存空对象
public Product 查商品(String productId) {
   Product product = 数据库查不到的方法();
   return product != null ? product : new Product("暂无数据");
}

遇到疯狂刷不存在的ID?给他返回"空气商品",让他刷个寂寞!


4.2 缓存雪崩:避免集体扑街

@Bean
public RedisCacheManager 随机过期大法(RedisConnectionFactory factory) {
    return RedisCacheManager.builder(factory)
        .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30 + new Random().nextInt(10)))) // 随机过期时间
        .build();
}

别让所有缓存像大学宿舍一样集体断电,给每个缓存不同的"死亡时间"!


五、性能玄学:让缓存飞一会儿

5.1 缓存预热:双十一生存指南'

@PostConstruct // 服务启动时执行
public void 偷偷加载缓存() {
    List<热门商品> items = 偷偷查数据库();
    items.forEach(item -> 缓存起来(item.getId(), item));
}

就像提前把爆款商品堆在超市门口,开门瞬间直接开抢!


5.2 多级缓存:俄罗斯套娃策略

记住:


80%的请求在本地小卖部解决(L1缓存)

15%去中央超市逛逛(Redis)

只有5%的倒霉蛋要去仓库搬砖(数据库)


六、修仙渡劫:常见天雷滚滚

6.1 缓存失效之谜

症状:明明调了@CacheEvict,缓存还在躺尸

把脉:


检查方法是不是被final修饰(AOP代理不了)

类内部方法调用(就像自己薅自己头发)

异常导致事务回滚(白删了)


6.2 序列化鬼打墙

症状:Redis里存的值像天书

药方:

@Bean
public RedisTemplate<String, Object> 防乱码模板() {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setKeySerializer(new StringRedisSerializer()); // 钥匙别乱改
    template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); // JSON大法好
    return template;
}

七、飞升指南:缓存大神的自我修养



命名规范:


好的缓存名:user:profile:1001(用户档案)

坏的名字:cache1(这是要给后人留坑?)




监控三件套:


命中率(摸鱼成功率)

响应时间(摸鱼速度)

内存使用(小卖部容量)

https://www.zuocode.com



防御性编程:


缓存操作加try-catch(就像给零食柜上锁)

设置降级策略(没薯片就改吃辣条)


作者:小厂永远得不到的男人

链接:https://juejin.cn