SpringCache
第一步:引依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
第二步:加配置
spring.cache.type=redis
spring.cache.redis.time-to-live=3600000
spring.cache.redis.key-prefix=CACHE_ # 如果指定了前缀就用我们指定的前缀,没有就默认使用缓存的名字作为前缀
spring.cache.redis.cache-null-values=true # 是否缓存空值,防止缓存穿透
# spring.cache.redis.use-key-prefix=false # 一般都不用
第三步:主方法加@EnableCaching
常用注解方法
1. @Cacheable({"category", "xxxx"})
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
举个栗子
1. @Cacheable(value="category", key="'levelCategories'")
缓存到数据库中的是 category::levelCategories
2. @Cacheable(value="category", key="#root.method.name")
缓存到数据库中的是 category::方法名
@Service
public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
@Override
@Cacheable(value = "banner", key = "'selectIndexList'")
public List<CrmBanner> selectAllBanner() {
LambdaQueryWrapper<CrmBanner> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(CrmBanner::getId).last("limit 2");
return list(queryWrapper);
}
}
2. @CacheEvict
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
即更新数据库后马上清空缓存,删除数据库后也马上清除缓存,以便下次查询重新获取缓存
allEntries属性
@CacheEvict(value="category", allEntries = true)
存储同一个类型的数据,都可以指定成一个分区,分区名默认就是缓存的前缀
3. @CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存
中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
4. @Caching
多个清空缓存操作在一个方法上,用@Caching
举个栗子
@Caching(evict = {
@CacheEvict(value="category", key="'getLevelCategories'"),
@CacheEvict(value="category", key="'getCatelogJson'")
}) 用这个方法标注的方法一执行就会清空redis中category::getLevelCategories和category::getCatelogJson的数据
Tips:key是SPEL表达式,如果要输入字符串,记得双引号里面再加一个单引号,其他要在加#
原理:
CacheAutoConfiguration --> RedisCacheConfiguration --> 自动配置了RedisCacheManager
---> 初始化所有缓存 --> 每个缓存决定使用什么配置
---> 如果redisCacheConfiguration有就用己有的,没有就用默认配置
---> 想要改缓存的配置,只需要给容器中放一个RedisCacheConfiguration即可
Spring-Cache的不足
1)读模式:
缓存穿透:查询一个null数据
解决:SpringCache在配置文件中添加spring.cache.redis.cache-null-values=true即可缓存空数据
缓存击穿:热点key问题,大量并发进来同时查询一个正好过期的数据
解决:SpringCache默认是无加锁的,我们只需要加上 sync=true即可加锁
@Cacheable(value={"category"},key="#root.method.name", sync=true)
(加本地锁,非分布式锁,但是也够了,并发量最多等于服务器数量无妨)
缓存雪崩:缓存中大量数据同时过期
解决:加随机时间 配置文件中加spring.cache.redis.time-to-live=3600000
2)写模式:
1. 读写加锁
-
引入Canal,感知到MySQL的更新去更新数据库
-
读多写多,直接去数据库查就行了
总结:
常规数据(读多写少,即时性,一致性要求不高的数据)
完全可以使用spring-cache, 写模式(只要缓存的数据有过期时间就足够了)
特殊数据:特殊设计
原理:CacheManager(RedisCacheManager) --> Cache(RedisCache) --> Cache负责缓存的读写