飞道的博客

Spring Cache

351人阅读  评论(0)

1、简介

 Spring 从 3.1 开始定义了 org.springframework.cache.Cache

和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用 JCache(JSR-107)注解简化我们开发;

 Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合;

Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache , EhCacheCache , ConcurrentMapCache 等;

 每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已 经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓 存结果后返回给用户。下次调用直接从缓存中获取。

 使用 Spring 缓存抽象时我们需要关注以下两点;

 1、确定方法需要被缓存以及他们的缓存策略

 2、从缓存中读取之前缓存存储的数据

2、基础概念 

 3、注解

 

4、表达式语法 

 5、项目使用

pom文件导入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

启动类开启缓存 


  
  1. @SpringBootApplication
  2. @EnableDiscoveryClient
  3. @EnableFeignClients
  4. @EnableCaching
  5. public class GulimallProductApplication {
  6. public static void main (String[] args) {
  7. SpringApplication.run(GulimallProductApplication.class, args);
  8. }
  9. }

 

写配置

1)自动配置了那些
  CacheAutoConfiguration导入了RedisCacheConfiguration
  自动配好了缓存管理器RdisCacheManager


  
  1. //将配置文件中的所有配置都生效
  2. @EnableConfigurationProperties(CacheProperties.class)
  3. @Configuration
  4. @EnableCaching
  5. public class MyCacheConfig {
  6. @Bean
  7. public RedisCacheConfiguration redisCacheConfiguration (CacheProperties cacheProperties){
  8. RedisCacheConfiguration config= RedisCacheConfiguration.defaultCacheConfig();
  9. // config.entryTtl()
  10. config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer( new StringRedisSerializer()));
  11. config=config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer()));
  12. CacheProperties. Redis redisProperties = cacheProperties.getRedis();
  13. if (redisProperties.getTimeToLive() != null) {
  14. config = config.entryTtl(redisProperties.getTimeToLive());
  15. }
  16. if (redisProperties.getKeyPrefix() != null) {
  17. config = config.prefixKeysWith(redisProperties.getKeyPrefix());
  18. }
  19. if (!redisProperties.isCacheNullValues()) {
  20. config = config.disableCachingNullValues();
  21. }
  22. if (!redisProperties.isUseKeyPrefix()) {
  23. config = config.disableKeyPrefix();
  24. }
  25. //将配置文件中的所有配置都生效
  26. return config;
  27. }
  28. }


2) 配置使用redis作为spring.cache.type=REDIS

# 应用名称
spring.application.name=gulimall-product
# 应用服务 WEB 访问端口
server.port=10001
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

spring.cache.type=REDIS
spring.cache.redis.time-to-live=3600000
#如果指定了前缀的值就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀
#spring.cache.redis.key-prefix=CACHE_
#spring.cache.redis.use-key-prefix=true
#缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true

 业务代码使用


  
  1. @Cacheable (value = { "category"},key = "#root.method.name",sync = true)
  2. @Override
  3. public List<CategoryEntity> getLevel1Categorys () {
  4. QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
  5. wrapper.eq( "parent_cid", 0);
  6. List<CategoryEntity> categoryEntities = baseMapper.selectList(wrapper);
  7. return categoryEntities;
  8. }
//每一个缓存的数据需要我们来指定要放到那个名字的缓存【缓存的分区(按照业务类型区分)】
//代表当前办法的结果需要缓存,如果缓存中有,方法不调用,如果缓存中没有,会调用方法,最后将方法的结果返回 

  
  1. @CacheEvict(value ="category",allEntries = true ) //删除分区全部缓存
  2. @Override
  3. public void updateCascade (CategoryEntity category) {
  4. this.updateById(category);
  5. categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
  6. }

 

@Caching(evict = {
        @CacheEvict(value = "category",key = "'getLevel1Categorys'"),
        @CacheEvict(value = "category",key = "'getCatalogJson'")
})

上面整个代码意思是category的getLevel1Categorys和getCatalogJson的数据都需要修改

/**
 * @Cacheable ({"category"})
 *     代表当前办法的结果需要缓存,如果缓存中有,方法不调用
 *     如果缓存中没有,会调用方法,最后将方法的结果返回
 * 默认行为:
 *      1)如果缓存中有,方法不调用
 *      key是默认自动生成的,缓存的名字:SimpleKey【】自动生成的key
 *      value是jdk序列化机制,将序列化后的数据存到redis
 *      默认过期时间ttl -1
 *  自定义
 *      1)指定生成的缓存使用的key  key属性指定(可以使用表达:key = "#root.method.name")
 *      2)指定缓存的数据的存活时间 配置文件指定 spring.cache.redis.time-to-live=3600000毫秒为单位
 *      3)将数据保存为json格式
 *          CacheAutoConfiguration->RedisCacheConfiguration
 *          ->自动配置了RedisCacheManage->初始化所有缓存->每个缓存决定使用什么配置
 *          ->如果redisCacheConfiguration有就用己有的,没有就用默认的
 *          ->想改缓存配置,只需要给容器中放一个RedisCacheConfiguration
 *   spring-Cache的不足:
 *   1)读模式
 *      缓存穿透:查询null,解决:缓存空数据 cahe-null-values=true
 *      缓存击穿:大量并发查询一个整过期的数据, 解决:加锁 sync = true
 *      缓存雪崩:大量过期的key同时过期        解决:加随机时间
 *   2)写模式
 *      1)读写加锁
 *      2)引入canal,感知mysql的更新的数据库
 *      3)读多写多 直接去数据库查询就可以
 *  总结:常规数据(读多写少的,一致性要求不高的数据)完全可以用spring-Cache
 *      特殊数据特殊处理
 *

  

 

缓存失效问题

先来解决大并发读情况下的缓存失效问题;

1、缓存穿透

 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数 据库也无此记录,我们没有将这次查询的 null 写入缓存,这将导致这个不存在的数据每次 请求都要到存储层去查询,失去了缓存的意义。

 在流量大时,可能 DB 就挂掉了,要是有人利用不存在的 key 频繁攻击我们的应用,这就是 漏洞。

 解决: 缓存空结果、并且设置短的过期时间。

2、缓存雪崩

 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失 效,请求全部转发到 DB,DB 瞬时压力过重雪崩。

 解决:

原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的 重复率就会降低,就很难引发集体失效的事件。

3、缓存击穿

 对于一些设置了过期时间的 key,如果这些 key 可能会在某些时间点被超高并发地访问, 是一种非常“热点”的数据。

 这个时候,需要考虑一个问题:如果这个 key 在大量请求同时进来前正好失效,那么所 有对这个 key 的数据查询都落到 db,我们称为缓存击穿。

 解决: 加锁


转载:https://blog.csdn.net/m0_62436868/article/details/128768514
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场