• Spring Cache和redis结合使用


    1 、简介
    Spring 定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术,并支持使用注解简化我们开发;
    Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合;
    Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache EhCacheCache , ConcurrentMapCache 等;
    每次调用需要缓存功能的方法时, Spring 会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
    使用 Spring 缓存抽象时需要关注以下两点:确定方法需要被缓存以及他们的缓存策略,从缓存中读取之前缓存存储的数据。
    2、相关操作
    2.1引入相关依赖
    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-cache</artifactId>
    4. </dependency>
    5. <dependency>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-data-redis</artifactId>
    8. </dependency>

    2.2配置文件application.properties

    1. #名字可以自动配置
    2. #spring.cache.cache-names=qq
    3. spring.cache.type=redis
    4. #设定数据存活时间1小时
    5. spring.cache.redis.time-to-live=3600000
    6. #key的前缀
    7. spring.cache.redis.key-prefix=CACHE_
    8. #key是否用前缀
    9. spring.cache.redis.use-key-prefix=false
    10. #是否缓存空值,防止缓存穿透
    11. spring.cache.redis.cache-null-values=true

    2.3自行配置RedisCacheConfiguration(如果不配置需要将@EnableCaching放到启动类上)

    1. package com.atguigu.gulimall.product.config;
    2. import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
    3. import org.springframework.boot.autoconfigure.cache.CacheProperties;
    4. import org.springframework.boot.context.properties.EnableConfigurationProperties;
    5. import org.springframework.cache.annotation.EnableCaching;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. import org.springframework.data.redis.cache.RedisCacheConfiguration;
    9. import org.springframework.data.redis.serializer.RedisSerializationContext;
    10. import org.springframework.data.redis.serializer.StringRedisSerializer;
    11. @EnableConfigurationProperties(CacheProperties.class)
    12. @Configuration
    13. @EnableCaching
    14. public class MyCacheConfig {
    15. @Bean
    16. RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
    17. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
    18. //设置key用string类型保存,value用json格式保存
    19. config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
    20. config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
    21. CacheProperties.Redis redisProperties = cacheProperties.getRedis();
    22. //使配置文件中所有的配置都生效
    23. if (redisProperties.getTimeToLive() != null) {
    24. config = config.entryTtl(redisProperties.getTimeToLive());
    25. }
    26. if (redisProperties.getKeyPrefix() != null) {
    27. config = config.prefixKeysWith(redisProperties.getKeyPrefix());
    28. }
    29. if (!redisProperties.isCacheNullValues()) {
    30. config = config.disableCachingNullValues();
    31. }
    32. if (!redisProperties.isUseKeyPrefix()) {
    33. config = config.disableKeyPrefix();
    34. }
    35. return config;
    36. }
    37. }

    2.4使用注解操作

    1. /**
    2. * 每一个需要缓存的数据都要指定要放到哪个名字的缓存。【缓存的分区(按照业务类型分)】
    3. * 代表当前方法的结果需要缓存,如果缓存中有,方法不会调用,如果缓存中没有,会调用方法。并将方法的结果放入缓存
    4. * 默认行为
    5. * 1)如果缓存中有,方法不再调用
    6. * 2)key是默认生成的:缓存的名字::SimpleKey::[](自动生成key值)
    7. * 3)缓存的value值,默认使用jdk序列化机制,将序列化的数据存到redis中
    8. * 4)默认ttl时间是 -1:
    9. *
    10. * 用户自行操作:key的生成
    11. * 1)指定生成缓存的key:key属性可以指定,接收一个Spel
    12. * 2)指定缓存的数据的存活时间:配置文档中修改存活时间
    13. * 3)将数据保存为json格式
    14. * CacheAutoConfiguration->RedisCacheConfiguration->自动配置了RedisCacheManager->初始化所有的缓存
    15. * -》每个缓存决定使用什么配置->如果redisCacheConfiguration有就用已有的,没有就用默认的配置->想改缓存配置,只需给容器
    16. * 中放一个RedisCacheConfiguration即可->就会应用到当前RedisCacheManager管理的所有缓存分区中
    17. *
    18. */
    19. @Cacheable(value={"category"},key="#root.method.name",sync = true)
    20. @Override
    21. public List getLevel1Categorys() {
    22. return this.baseMapper.selectList(new QueryWrapper().eq("parent_cid",0));
    23. }

    2.5访问redis数据库

    2.6再添加两个方法

    1. @Cacheable(value = "category",key="#root.methodName")
    2. @Override
    3. public Map> getCatelogJson() {
    4. //业务操作。。。
    5. }
    1. //多项操作时用@Caching
    2. @Caching(evict = {
    3. @CacheEvict(value = "category",key="'getLevelCategorys'"),
    4. @CacheEvict(value = "category",key="'getCatelogJson'")
    5. })
    6. @Transactional
    7. @Override
    8. public void updateCascade(CategoryEntity category) {
    9. this.updateById(category);
    10. System.out.println(category.getCatId());
    11. System.out.println(category.getName());
    12. categoryBrandRelationService.updateCatelog(category.getCatId(),category.getName());
    13. }

    2.7运行测试

    先执行getLevel1Categorys()和getCatelogJson()两个方法,可以看到redis数据库里存了它们缓存的数据

    接着运行 updateCascade()方法,则数据消失了。@Caching可同时进行多项操作,@CacheEvict可以删除指定的缓存。

    补充:将

    @Caching(evict = {
            @CacheEvict(value = "category",key="'getLevelCategorys'"),
            @CacheEvict(value = "category",key="'getCatelogJson'")
    })

    替换成

    @CacheEvict(value = "category",allEntries = true) //删除某个分区下的所有数据

    可以获得同样的效果。

    同时,将配置文件替换成:

    1. #名字可以自动配置
    2. #spring.cache.cache-names=qq
    3. spring.cache.type=redis
    4. #设定数据存活时间1小时
    5. spring.cache.redis.time-to-live=3600000
    6. #key的前缀
    7. #spring.cache.redis.key-prefix=CACHE_
    8. #key是否用前缀
    9. spring.cache.redis.use-key-prefix=true
    10. #是否缓存空值,防止缓存穿透
    11. spring.cache.redis.cache-null-values=true

    执行执行getLevel1Categorys()和getCatelogJson()两个方法,可以看到数据库里两个缓存数据归类到category下面:

     3、SpringCache的不足之处

    1)、读模式
    缓存穿透:查询一个null数据。解决方案:缓存空数据。

    spring.cache.redis.cache-null-values=true

    缓存击穿:大量并发进来同时查询一个正好过期的数据。解决方案:加锁 ? 默认是无加锁的;使用sync = true来解决击穿问题。

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

    缓存雪崩:大量的key同时过期。解决:加随机时间。加上过期时间。

    spring.cache.redis.time-to-live=3600000

    2)、写模式:(缓存与数据库一致)
    读写加锁。
    引入Canal,感知到MySQL的更新去更新Redis
    读多写多,直接去数据库查询就行

    4、总结

    常规数据(读多写少,即时性,一致性要求不高的数据,完全可以使用Spring-Cache):写模式(只要缓存的数据有过期时间就足够了)

    原理:CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache负责缓存的读写

  • 相关阅读:
    AI Studio星河社区生产力实践:基于文心一言快速搭建知识库问答
    shell_56.Linux永久重定向
    『力扣每日一题16』:存在重复元素
    Elasticsearch中可避免的坑
    【镜像制作】node.js+pm2的latest版镜像制作
    存在重复元素(简单)
    「小邓观点」SIEM解决方案的数据聚合组件
    如何按照指定边界(范围)裁剪高程区域
    Linux——基础IO
    【实验】Hadoop-2.7.2+zookeeper-3.4.6完全分布式环境搭建(HDFS、YARN HA)转载
  • 原文地址:https://blog.csdn.net/xushuai2333333/article/details/126659938