一、缓存世界的生存法则
1.1 缓存是什么?程序员的"小卖部哲学"
想象你是个996的社畜程序员,每次想喝肥宅快乐水都要跑到3公里外的仓库(数据库)去拿,直到有一天...
你在工位旁开了个小卖部(缓存)!
- 高频访问:肥宅水、泡面常备(热点数据)
- 快速响应:伸手就能拿到(内存级速度)
- 过期策略:薯片每周五补货(TTL时间)
这就是Spring Cache的奥义------让数据库这个"大冤种"少跑腿!
1.2 三大护法注解(缓存界的桃园三结义)
注解 | 江湖绝技 | 口头禅 |
---|---|---|
@Cacheable | 乾坤大挪移(优先取缓存) | "这题我见过,直接抄答案!" |
@CachePut | 移花接木(强制更新缓存) | "旧的不去新的不来!" |
@CacheEvict | 化骨绵掌(删除缓存) | "毁灭吧,赶紧的!" |
二、Redis合体大法(本地缓存VS分布式)
2.1 单机模式:小卖部的自我修养
java
// 配置本地缓存(Caffeine)
spring.cache.type=caffeine
spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=5m
就像在工位抽屉里藏零食,优点是伸手就能摸到,缺点是换工位(重启服务)就没了...
2.2 集群模式:开连锁超市
yaml
spring:
cache:
type: redis
redis:
time-to-live: 30m # 商品保质期
key-prefix: "BUFF:" # 货架标签
现在你是连锁超市老板(Redis集群),全公司程序猿都能来蹭吃蹭喝,但记得:
- 别卖过期食品(设置TTL)
- 做好防盗措施(密码配置)
- 别把货架堆太满(内存限制)
三、注解实战:缓存界的"摸鱼"技巧
3.1 @Cacheable的正确躺姿
java
@Cacheable(value = "外卖",
key = "#userId + '_' + #restaurantId",
unless = "#result.price > 50") // 超过50块的不缓存
public String 点外卖(Long userId, String restaurantId) {
System.out.println("【真实伤害】数据库查询中...");
return "香辣鸡腿堡套餐";
}
当同事问你为什么总不写SQL------"因为我在摸鱼啊(缓存命中)!"
3.2 缓存清理的十八种姿势
java
@CacheEvict(value = "购物车",
allEntries = true, // 清空整个货架
beforeInvocation = true) // 双十一前先清缓存
public void 结算购物车(Long userId) {
// 支付成功后让缓存原地爆炸
}
记住:删缓存要像分手一样干脆,别给脏数据留机会!
四、防坑指南:缓存世界的黑暗森林
4.1 缓存穿透:防羊毛党攻略
java
@Cacheable(value = "商品详情",
unless = "#result == null") // 缓存空对象
public Product 查商品(String productId) {
Product product = 数据库查不到的方法();
return product != null ? product : new Product("暂无数据");
}
遇到疯狂刷不存在的ID?给他返回"空气商品",让他刷个寂寞!
4.2 缓存雪崩:避免集体扑街
java
@Bean
public RedisCacheManager 随机过期大法(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30 + new Random().nextInt(10)))) // 随机过期时间
.build();
}
别让所有缓存像大学宿舍一样集体断电,给每个缓存不同的"死亡时间"!
五、性能玄学:让缓存飞一会儿
5.1 缓存预热:双十一生存指南
java
@PostConstruct // 服务启动时执行
public void 偷偷加载缓存() {
List<热门商品> items = 偷偷查数据库();
items.forEach(item -> 缓存起来(item.getId(), item));
}
就像提前把爆款商品堆在超市门口,开门瞬间直接开抢!
5.2 多级缓存:俄罗斯套娃策略
graph LR
用户 --> 本地小卖部(Caffeine)
本地小卖部 --> 中央超市(Redis)
中央超市 --> 终极仓库(MySQL)
记住:
- 80%的请求在本地小卖部解决(L1缓存)
- 15%去中央超市逛逛(Redis)
- 只有5%的倒霉蛋要去仓库搬砖(数据库)
六、修仙渡劫:常见天雷滚滚
6.1 缓存失效之谜
症状 :明明调了@CacheEvict,缓存还在躺尸
把脉:
- 检查方法是不是被final修饰(AOP代理不了)
- 类内部方法调用(就像自己薅自己头发)
- 异常导致事务回滚(白删了)
6.2 序列化鬼打墙
症状 :Redis里存的值像天书
药方:
java
@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
(这是要给后人留坑?)
- 好的缓存名:
-
监控三件套:
- 命中率(摸鱼成功率)
- 响应时间(摸鱼速度)
- 内存使用(小卖部容量)
-
防御性编程:
- 缓存操作加try-catch(就像给零食柜上锁)
- 设置降级策略(没薯片就改吃辣条)
最后送上终极奥义:
缓存不是银弹,乱用缓存一时爽,数据一致火葬场!
各位缓存修仙者,你在闯荡江湖时遇到过哪些奇葩缓存问题?是缓存击穿让你一夜秃头,还是缓存雪崩让你怀疑人生?欢迎在评论区分享你的渡劫经历,点赞最高的道友将获得【防脱发洗发水】一瓶(虚拟版)!