SpringCache基本知识

SpringBoot整合SpringCache

SpringCache整合多种数据源,使用Aop提供业务缓存的使用,使用注解既可以实现简单的应用缓存配置。

Spring官方参考文档

1、引入依赖POM

1.1、引入SpringBoot-Cache

//引入SpringCache官方Starter
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
//使用SpringCache中什么样的数据源就引入什么类型的启动器

1.2、引入第三方的缓存Jar依赖

根据SpringBoot CacheAutoConfiguraton分析可知,其自动配置了很多缓存的数据源:

image-20220411210146061

而这些数据自动配置类,在导入响应的Jar依赖之后会自动配置,这里使用Redis作为缓存中间件。

//父工程为SpringBoot自动管理依赖版本
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在自动配置类中可以发现绑定了许多的属性配置文件,也就是对于SpringCache的配置可以用“spring.cache”开头的属性进行配置。

image-20220411210544395

进入到RedisAutoConfiguration.class 中可以发现其绑定了有关Redis的配置在

image-20220411210641708

因此可以在配置文件中配置有关Redis的配置项目以“spring.redis”前缀进行匹配。

2、编写配置

2.1、配置原理

​ 在SpringCache自动配置类中还会注入一个配置 CacheConfigurationImportSelector缓存配置自动选择:器会根据导入的缓存进行选择性自动配置。

image-20220411211424247

进入RedisCacheConfiguration.class配置类中,可以发现其默认使用Redisd的默认配置,并且将我们在配置类中配置的有关Redisd的配置导入到了这个RedisCacheConfiguration中(配置类中的属性,在CacheAutoConfiguraton已经注入到容器之中)

image-20220411212038392

2.2、配置方式—注入配置类

2.2.1、配置application.yaml
sping:
  #配置SpringCache
  cache:
    type: redis
    redis:
      time-to-live: 3600000
      #      缓存前缀
      #      key-prefix: CACHE_
      #      开启缓存前缀,不指定缓存前缀->缓存名称::自主生成key值 指定了前缀 前缀+缓存名称::自主生成key值
      use-key-prefix: true
      #      缓存空值是否开启--解决缓存穿透(redis中没有key值)
      cache-null-values: true
  #    引入redis配置
  redis:
    host: localhost
    port: 6379   
2.2.1、老版本

1、高版本在缓存自动配置类已经注入了配置文件绑定类

2、老版本在这个配置类中开启@EnableConfigurationProperties将CacheProperties.class注入容器,获取配置文件中配置

@EnableCaching
@Configuration
public class MyRedisCacheConfig {
    /**
     * 注入一个 RedisCacheConfiguration 链式调用(旧版写法)
     */
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        //链式调用,每次要覆盖原来的
        redisCacheConfiguration =
                redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
        //获取有关redis的配置getRedis()
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        //获取配置文件中的值参考RedisCacheConfiguration写法
        if (redisProperties.getTimeToLive() != null) {
            redisCacheConfiguration = redisCacheConfiguration.entryTtl(redisProperties.getTimeToLive());
        }

        if (redisProperties.getKeyPrefix() != null) {
            redisCacheConfiguration = redisCacheConfiguration.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }

        if (!redisProperties.isCacheNullValues()) {
            redisCacheConfiguration = redisCacheConfiguration.disableCachingNullValues();
        }

        if (!redisProperties.isUseKeyPrefix()) {
            redisCacheConfiguration = redisCacheConfiguration.disableKeyPrefix();
        }
        return redisCacheConfiguration;
    }
}
2.2.2、新版本

完全控制redis cache configuration 针对每个cacheName进行配置

    /**
     * 参考地址:https://www.baeldung.com/spring-boot-redis-cache
     * @return RedisCacheManagerBuilderCustomizer
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -> builder
                .withCacheConfiguration("itemCache",
                        RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)))
                .withCacheConfiguration("customerCache",
                        RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)));
    }

3、使用注解缓存

  • 使用缓存:
    • 1、每一个需要缓存数据都需要指定放到哪个缓存分区下【缓存分区(按照业务划分)】

    • 2、表示当前方法结果需要缓存,缓存中存在,方法不会进行调用,没有就会调用方法,最后将方法结果存入缓存

    • 3、默认行为

      • 1、缓存中存在,方法不调用
      • 2、默认生成key category::SimpleKey [] _—>缓存名称(value)::自主生成key值(key)
      • 3、缓存的值(不配置使用Java默认序列化器序列化)
    • 4、默认时间为-1用不过期 TTL=-1

    • 自定义

      • 1、指定生成缓存使用的key值,使用key属性设置,接受一个SPEL表达式 参考https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-cacheable

      • 2、指定缓存的数据的TTL 再配置文件中设置 spring.cache.redis.time-to-live 单位是ms

      • 3、自定义序列化!!多个系统之间数据交互使用JSON 自定义缓存管理器

        • 缓存自动配置导入RedisCacheConfiguration -> redisCacheManager(determineConfiguration(决定每个缓存使用怎样的配置)->判断redisCacheConfiguration是不是空的,空的就使用默认的)

          image-20220411214000504

        //使用FastJson进行序列化反序列化
        redisCacheConfiguration =              redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
        
  • @Cacheable 触发将数据保存到缓存

    @Cacheable(value = "category", key = "#root.methodName", sync = true)
    //value:使用的缓存分区名称
    //key:使用存储的缓存名称key值(#root为spel表达式)
    //sync;表示增加同步锁(本地)在查询的时候会先查询缓存,缓存中没有的时候会去数据库中查询——>其将这个过程进行加锁
    
    

​ 查找原理:

  1. 缓存不存在

    image-20220411231111856

  2. 缓存存在

    image-20220411230327263

  3. 开启同步Sync

    1. 缓存中不存在(会使用同步方法getSynchronized进行查询,这个方法中会调用查询数据库,之后put设置到缓存中)

      image-20220411225205974

    2. 缓存中存在(直接取,取不加锁)

  • @CacheEvict 触发缓存删除

    @CacheEvict(value = {"缓存分区名称"}, key = "'删除的Key值(字符串使用''括起来)'")
    @CacheEvict(value = "缓存分区名称",allEntries = true) //allEntries删除全部缓存信息
    
  • @CachePut 更新缓存信息,并且不会影响方法执行

  • @Caching 组合多个缓存操作,比如又更新又删除,又将这个缓存移到其他的里面

        @Caching(evict = {
                @CacheEvict(value = {"缓存分区"}, key = "'缓存Key'"),
                @CacheEvict(value = {"缓存分区"}, key = "'缓存key'")
        })
    
  • @CacheConfig 在类级别共享缓存相同配置

4、开启缓存

@EnableCaching //开启缓存注解
@Configuration //配置类
public class MyRedisCacheConfig {
}

5、SpringCache总结

  1. 读模式
    1. 缓存穿透(null)----> use-key-prefix: true (允许设置空值解决) ✔️
    2. 缓存击穿 sync功能解决 ✔️
    3. 缓存雪崩 time-to-live: 3600000(设置过期时间解决)✔️
  2. 写模式(缓存与数据库强一致)❌(均需要手动实现)
    1. 读写加锁 ❌
    2. 使用Canal(中间件感知到MySql更新去更新数据库 ----> 伪装为一个mysql slave欺骗mysql master给自己推送中继日志,从而实现增量获取事件的功能)❌
    3. 读多写多(直接去数据库)❌

1、读多写少,及时性,一致性要求不高的数据

2、实时性要求高,加缓存,必须特殊设计!

6、备注–常用注解参数

image-20220411233259038

7、备注–常用SPEL表达式

image-20220411233230934

slave欺骗mysql master给自己推送中继日志,从而实现增量获取事件的功能)❌
3. 读多写多(直接去数据库)❌

1、读多写少,及时性,一致性要求不高的数据

2、实时性要求高,加缓存,必须特殊设计!

6、备注–常用注解参数

[外链图片转存中…(img-r8dei7Cn-1663526688534)]

7、备注–常用SPEL表达式

[外链图片转存中…(img-I2uZmcG6-1663526688534)]

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会拯救世界的SuperMan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值