乔人尹 - Annotation
type: channel
link: https://underestimated.cn/tag/Annotation/
items: Cacheable注解缓存的使用

Cacheable注解缓存的使用
type: item
date: 2022-09-18T01:00:00+00:00
description: 前言：一个惨痛的教训话说那年，我写了一个"用户信息查询"接口，代码简洁优雅，自我感觉良好。上线后 QPS 一路飙升，数据库 CPU 直接拉满，DBA 小哥深夜给我打电话，语气中透着一丝疲惫和杀意。"兄弟，你这接口一秒钟查同一个用户几百次，图啥呢？"那一刻，我顿悟了：缓存，是程序员对数据库最基本的尊重。一、@Cacheable 是什么？@Cacheable 是 Spring Cache 提供的声明式缓存注解，属于"我不想写重复代码"系列的终极解决方案。它的核心思想很朴素：第一次调用方法 → 执行方法，结果存缓存后续相同参数调用 → 直接返回缓存，方法体压根不执行简单说就是："这活我干过了，答案在这，别烦我。"二、快速上手2.1 添加依赖 org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-data-redis 2.2 开启缓存功能在启动类上加一个注解，仪式感要有：@SpringBootApplication @EnableCaching // 就这一行，缓存世界的大门向你敞开 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }2.3 使用 @Cacheable@Service public class UserService { @Cacheable(value = "userCache", key = "#userId") public User getUserById(Long userId) { // 这行日志是关键，用来验证缓存是否生效 System.out.println(">>> 真的去查数据库了，userId: " + userId); return userRepository.findById(userId).orElse(null); } }测试一下：userService.getUserById(1L); // 控制台输出日志，查了数据库 userService.getUserById(1L); // 静悄悄，缓存命中 userService.getUserById(1L); // 依然静悄悄 userService.getUserById(2L); // 输出日志，新用户要查库恭喜，你的数据库松了一口气。三、核心属性详解@Cacheable 的属性不多，但每个都值得细品。3.1 value / cacheNames —— 缓存的"收纳盒"@Cacheable(value = "userCache") @Cacheable(cacheNames = "userCache") // 两种写法等价 @Cacheable(value = {"cache1", "cache2"}) // 可以同时放多个盒子这玩意就是给缓存分类，就像你家的收纳盒：袜子放一个、内裤放一个，别混了。3.2 key —— 缓存的"门牌号"默认情况下，Spring 会用方法的所有参数生成 key。但很多时候我们需要自定义：// 使用 SpEL 表达式 @Cacheable(value = "userCache", key = "#userId") public User getUserById(Long userId) { ... } // 多参数组合 @Cacheable(value = "userCache", key = "#userId + '_' + #type") public User getUserByIdAndType(Long userId, String type) { ... } // 使用对象的属性 @Cacheable(value = "userCache", key = "#user.id") public User updateUser(User user) { ... } // 使用方法名 + 参数（防止不同方法 key 冲突） @Cacheable(value = "userCache", key = "'getUser_' + #userId") public User getUserById(Long userId) { ... }踩坑预警：key 冲突是缓存的头号杀手。想象一下，getUserById(1L) 和 getOrderById(1L) 用了同一个 key，返回数据直接串台，这 bug 够你查三天。3.3 condition —— 有条件地缓存不是所有数据都值得缓存，…