性能狂飆!Spring Boot 基于注解的八個緩存應(yīng)用技巧
環(huán)境:SpringBoot3.4.2
1. 簡介
Spring Boot 基于注解的緩存機制,無疑是提升性能的強大助推器。本文將為你深度揭秘 8 個超實用的緩存應(yīng)用技巧。從合理運用不同緩存注解,如精準(zhǔn)使用 @Cacheable、@CachePut、@CacheEvict 實現(xiàn)緩存的增刪改查,到巧妙結(jié)合 Caffeine、Redis 等緩存組件,發(fā)揮它們各自優(yōu)勢構(gòu)建多級緩存體系;再到利用緩存策略,如設(shè)置合適的過期時間,優(yōu)化緩存使用。
掌握這些技巧,能讓你輕松打破性能瓶頸,讓 Spring Boot 應(yīng)用在數(shù)據(jù)處理的賽道上一路狂飆,為用戶帶來更流暢、高效的體驗。
2.實戰(zhàn)案例
2.1 @Cacheable緩存數(shù)據(jù)
如果一個方法使用了該注解,那么該方法(或該當(dāng)前類中的所有方法)的返回值都可以被緩存。
@Cacheable(key = "#user.id", cacheNames = {"users"})
public User save(User user) {
return user ;
}這里將會以 "users" 為key的前綴+User對象的id值作為整個緩存的key,存入緩存管理對象中(可以使用Caffeine或Redis),如下以Redis為例:
圖片
圖片
2.2 @CacheConfig緩存配置
@CacheConfig 可以在類上進行通用緩存設(shè)置。當(dāng)該注解應(yīng)用于某個類時,它會為該類中定義的任何緩存操作提供一組默認設(shè)置。如下示例:
@Service
@CacheConfig(cacheNames = {"users"})
public class UserService {
@Cacheable(key = "#user.id")
public User save(User user) {
return user ;
}
}如上配置,我們就可以在具體的方法上不用指定 cacheNames 屬性。該注解還可以配置如下的屬性:
public @interface CacheConfig {
String[] cacheNames() default {};
// 緩存key的生成方式
String keyGenerator() default "";
// 緩存管理對象,具體將緩存存入哪里
String cacheManager() default "";
// 緩存解析,如果解析獲取緩存對象Cache(緩存內(nèi)部都是通過Cache來管理緩存的)
String cacheResolver() default "";
}2.3 @CacheEvict刪除緩存
該注解用于指示某個方法(或某個類上的所有方法)會觸發(fā)緩存清除(cache evict)操作。如下示例:
@CacheEvict(cacheNames = "users", key = "#id")
public void deleteById(Long id) {
System.out.printf("刪除用戶【%d】%n", id) ;
}該示例將會刪除以 "users" 為前綴 + id 組成key 的緩存對象。
你也可以通過如下方式刪除所有緩存:
@CacheEvict(cacheNames = "users", allEntries = true)
public void deleteById(Long id) ;這將刪除 users 為前綴下的所有緩存數(shù)據(jù)。
2.4 @CachePut更新緩存
這個注解用在方法上(或者一個類上的所有方法),表示這個方法執(zhí)行完后,要把它的返回結(jié)果更新到緩存里。如下示例:
@CachePut(key = "#user.id")
public User updateUser(User user) {
return user ;
}該示例會更新緩存內(nèi)容,不管該key是否存在,不存在則存入方法當(dāng)前的返回值。
該注解與@Cacheable區(qū)別:
- @Cacheable:如果緩存中指定的key已經(jīng)存在,那么不會執(zhí)行方法
- @CachePut:不管指定的緩存key是否存在都將執(zhí)行方法,并且將最終方法的返回值更新到緩存。
2.5 自定義key生成
當(dāng)我們的key生成規(guī)則比較復(fù)雜時,我們可以自定義Key的生成策略,如下示例:
// 在沒有指定key的情況下
@Cacheable
public User save(User user)該示例中,我們并沒有指定key屬性,默認情況下生成的key如下:
圖片
自定義KeyGenerator
@Bean
KeyGenerator packKeyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return target.getClass().getSimpleName()
+ "@" + method.getName()
+ "@" + Arrays.deepToString(params) ;
}
};
}指定自定義的KeyGenerator
@Cacheable(keyGenerator = "packKeyGenerator")
public User save(User user)最終生成key
圖片
2.5 多級緩存
內(nèi)存訪問迅捷但容量受限,Redis 分布式擴展性強卻速度稍遜,構(gòu)建多級緩存可融合二者優(yōu)勢。如下查找流程:
- 首先,檢查內(nèi)存(EhCache、Caffeine)
- 如果未找到,則回退到 Redis
- 最后,僅在必要時訪問數(shù)據(jù)庫
這需要采用多級緩存管理器。你可以使用以下庫來實現(xiàn):
- Spring 緩存抽象層 + Caffeine + Redis
- [Bucket4j 或 Resilience4j](用于自定義策略)
2.6 緩存有效期TTL
切勿讓緩存永遠處于過期狀態(tài)。一定要設(shè)置超時。
關(guān)于緩存有效期,官方有如下說明:
直接通過你的緩存提供程序(來配置)。緩存抽象層是一種抽象,而非具體的緩存實現(xiàn)。你所采用的解決方案可能支持其他方案所不具備的各種數(shù)據(jù)策略和不同的拓撲結(jié)構(gòu)(例如,JDK 的 ConcurrentHashMap——若在緩存抽象層中暴露它,則毫無意義,因為沒有底層支持)。這類功能應(yīng)直接通過底層緩存(在配置時)或通過其原生 API 進行控制。
簡單說就是,具體是依賴你所使用的緩存實現(xiàn)有關(guān)。如下Redis緩存的有效期配置:
spring:
cache:
type: redis
redis:
time-to-live: 60s緩存有效期60s。
圖片
2.7 條件緩存
當(dāng)不是所有的數(shù)據(jù)都需要進行緩存時,我們可以通過配置condition屬性來指定緩存的條件,如下示例:
@Cacheable(keyGenerator = "packKeyGenerator",
condition = "#user.id != 666")
public User save(User user)如上示例,只有當(dāng)id不為666時才會緩存該方法的緩存值。
我們還可以根據(jù)方法的返回值來決定是否進行緩存,如下示例:
Cacheable(unless = "#result == null")
public User save(User user)如果當(dāng)前方法的返回值為null,則不進行緩存。
2.8 異步緩存刷新
與其刪除并等待用戶請求重新填充緩存,不如主動刷新緩存。我們可以通過定時任務(wù)來刷新緩存數(shù)據(jù),如下示例:
// 1小時執(zhí)行一次
private final CacheManager cacheManager ;
@Scheduled(fixedRate = 3600000)
public void refreshDictCache() {
List<Dict> dicts = dictRepository.findAll();
dicts.forEach(d -> cacheManager.getCache("dicts").put(d.getId(), d)) ;
}或者使用 Spring Events 在其他地方發(fā)生更新時觸發(fā)緩存重新填充。
































