缓存的使用、SpringCache简介、Springboot整合缓存-46

一:SpringCache简介

1)Spring 从 3.1 开始定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用 JCache(JSR-107)注解简化我们开发;
2)Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache , EhCacheCache , ConcurrentMapCache 等;
3)每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已 经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓 存结果后返回给用户。下次调用直接从缓存中获取。
4)使用 Spring 缓存抽象时我们需要关注以下两点;

  • 1、确定方法需要被缓存以及他们的缓存策略
  • 2、从缓存中读取之前缓存存储的数据

二:整合SpringCache简化缓存开发

1.引入依赖

      <!--引入缓存依赖-->
        
            org.springframework.boot
            spring-boot-starter-cache
        

2.写配置

1)自动配置了那些?

  • CacheAutoConfiguration会导入RedisCacheConfiguration——自动配置好了缓存管理器RedisCacheManager

2)我们需要配置那些?

  • 配置使用redis作为缓存——新建application.proerties文件,添加缓存类型配置
## 缓存的类型
spring.cache.type=redis

3.测试使用缓存

1)注解:
@Cacheable:触发将数据保存到缓存的操作
@CacheEvict:触发将数据从缓存删除的操作
@CachePut:不影响方法执行更新缓存
@Caching:组合多个缓存操作
@CacheConfig:在类级别,共享相同的缓存配置
2)开启缓存的功能——@EnableCaching

  • 使用@EnableCaching注解,开启使用缓存功能

3)只需要使用注解就可以使用缓存操作

  • 1.@Cacheable({“category”}):代表当前方法结果需要缓存,如果缓存有,方法不用调用。如果缓存没有,会调用方法,最后将方法的结果放入话缓存
  • 2.每一个需要缓存的数据我们都来指定放入那个名字的缓存。也就是缓存的分区(按照业务类型分)
  • 3.category::SimpleKey []:缓存的名字,SimpleKey是自主生成的key值
  • 4.缓存的value值。默认使用jdk序列化机制。将序列化后的数据存到redis中
  • 5.默认ttl时间是-1:也就是永不过期(不符合缓存的规范)
//每一个需要缓存的数据我们都来指定放入那个名字的缓存。也就是缓存的分区(按照业务类型分)
    @Cacheable({"category"})  //代表当前方法结果需要缓存,如果缓存有,方法不用调用。如果缓存没有,会调用方法,最后将方法的结果放入话缓存
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_cid",0);
        List<CategoryEntity> categoryEntities = baseMapper.selectList(wrapper);
        return categoryEntities;
    }

缓存的使用、SpringCache简介、Springboot整合缓存-46_第1张图片

4.@Cacheable注解——触发将数据保存到缓存的操作

1)指定生成的缓存使用指定的key——key属性指定,接受当前方法名作为key

@Cacheable(value = {"category"},key = "'getLevel1Categorys'")

动态接受,当前方法名作为key值

@Cacheable(value = {"category"},key = "#root.method.name")

2)指定缓存数据的存活时间——在配置文件中修改ttl

# 指定存活时间,单位毫秒,设定一个小时
spring.cache.redis.time-to-live=3600000

3)将数据保存为json格式
4)原理
CacheAutoConfiguration会导入RedisCacheConfiguration自动配置好了缓存管理器RedisCacheManager,RedisCacheManager会初始化所有缓存,每个缓存决定使用什么配置,有就使用已经存在的,没有的话就使用默认的。想改缓存的配置就给容器中放入一个RedisCacheConfiguration即可,就会应用到当前RedisCacheManager管理的缓存分区中。

5.加入MyCacheConfig配置类

1)将key和value转化为对应的格式

package com.sysg.gulimail.product.config;

        import org.springframework.cache.annotation.EnableCaching;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.data.redis.cache.RedisCacheConfiguration;
        import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
        import org.springframework.data.redis.serializer.RedisSerializationContext;
        import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching  //开启缓存功能
public class MyCacheConfig {
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //config = config.entryTtl();
        //序列化key
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //序列化value,存的值转化为json格式
        config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return config;
    }

}
  • 添加当前配置以后,数据就会保存为json格式

2)过期时间代码优化
1.问题:配置类添加好以后,配置文件中的配置没有生效

  • 原来的配置文件绑定的配置类是这样的
 @ConfigurationProperties(prefix = "spring.cache")
 public class CacheProperties{}

2.问题:如何让配置文件中的配置生效呢

  • @EnableConfigurationProperties(CacheProperties.class) //绑定配置文件中的配置,使其生效
package com.sysg.gulimail.product.config;
@Configuration
@EnableCaching  //开启缓存功能
@EnableConfigurationProperties(CacheProperties.class) //绑定配置文件中的配置,使其生效
public class MyCacheConfig {
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //config = config.entryTtl();
        //序列化key
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //序列化value
        config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        //将配置文件中的所有配置都让其生效
        /**
         * 1.原来绑定的配置类如下
         * @ConfigurationProperties(prefix = "spring.cache")
         * public class CacheProperties {
         */
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

3)优化application.properties配置文件

## 缓存的类型
spring.cache.type=redis
## 缓存的名字
## spring.cache.cache-names
# 指定存活时间,单位毫秒,设定一个小时
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

6.@CacheEvict(失效模式)——触发将数据从缓存删除的操作

1)更新代码

 /**
     * 级联更新所有关联的数据
     * @CacheEvict:失效模式
     * @param category
     */
    @CacheEvict(value = "category",key="'getLevel1Categorys'")
    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
        //更新自己
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
        //同时修改缓存中的数据
    }
  • 在定义key时,常量,常规字符串一点定要加单引号,否则就会报错
  • 只要后台修改了内容,那么缓存就会删除。直到下次重新请求首页

7.@Caching——组合多个缓存操作

1)方式一

@Caching(evict = {
            @CacheEvict(value = "category",key="'getLevel1Categorys'"),
            @CacheEvict(value = "category",key="'getCatalogJson'")
    })
    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
        //更新自己
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
        //同时修改缓存中的数据
    }
  • 同时进行多个缓存操作@Caching

2)方式二

  • @CacheEvict(value=“category”) //批量删除当前分区下的所有内容
  • 存储同一类型的数据,都可以指定成同一个分区

三:springCache原理与不足

1.读模式

1)缓存穿透:查询一个null数据。解决:缓存空数据。spring.cache.redis.cache-null-values=true
2)缓存击穿:大量并发进来同时查询一个正好过期的数据。解决:加锁
3)缓存雪崩:大量key同时过期。解决:加随机时间。加上过期时间。spring.cache.redis.time-to-live=3600000

2.写模式:(缓存与数据库一致)

1)读写加锁。适用于读多写少
2)引入canal,感知到mysql的更新去更新数据库
3)读多血多,直接去查询数据库就行

你可能感兴趣的:(redis,springcache,缓存,redis,java)