缓存-SpringCache

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、整合&体验@cacheable

导入依赖

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

在application.properties配置使用redis作为缓存

spring.cache.type = redis

在需要缓存的地方加注解@cacheable

    @Cacheable({"category"})
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }

3、自定义缓存配置

缓存key的值

复制代码
  /*默认行为:
如果缓存中有,方法不会调用
key默认自动生成:缓存的名字::SimpleKey[](自动生成的key值)
缓存的value的值,默认使用jdk序列化机制,将序列化后的数据存到redis
默认ttl时间为-1(永不过期)*/
  @Cacheable(value = {"category"},key = "#root.method.name")
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }
复制代码

配置缓存value序列化为json

复制代码
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyCacheConfig {

    @Autowired
    CacheProperties cacheProperties;

    @Bean
    RedisCacheConfiguration redisCacheConfiguration () {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        //将配置文件中的所有配置都生效
        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;
    }
}
复制代码

在application.properies中配置缓存空值防止缓存穿透

复制代码
#springCache使用缓存的类型
spring.cache.type=redis
#过期时间
spring.cache.redis.time-to-live=600000
#缓存key前缀
#spring.cache.redis.key-prefix=CACHE_
#是否开启使用缓存key前缀,如果指定了前缀就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀,即@cacheable(value)的value值
spring.cache.redis.use-key-prefix=true
#是否缓存空值。防止缓存穿透
spring.cache.redis.cache-null-values=true
复制代码

 @CacheEvict

失效模式:更新数据库,删除category分区下key为getLevel1Categorys的缓存

    @CacheEvict(value = "category",key = "'getLevel1Categorys'")
    @Override
    public void updateCascde(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
    }

删除category分区下的所有数据

    @CacheEvict(value = "category",allEntries = true)
    @Override
    public void updateCascde(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
    }

@Caching

同时进行多种缓存操作

 举例

复制代码
    @Caching(evict = {
            @CacheEvict(value = "category",key = "'getLevel1Categorys'"),
            @CacheEvict(value = "category",key = "'getCatelogJson'")
    })
    // @CacheEvict(value = "category",key = "'getLevel1Categorys'")
    @Override
    public void updateCascde(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
    }
复制代码

@CachePut

双写模式:更新数据库,也更新缓存

总结

读模式:

  • 缓存穿透:查询一个null数据。解决:缓存空数据;ache-null-value=true
  • 缓存击穿:大量并发进来同时查询一个正好过期的数据。解决:加锁;@cacheable默认无加锁;可以在@cacheable中加sync = true打开加锁
  • 缓存雪崩:大量的key同时过期。解决:加随机时间。加上过期时间即可(因为每个添加缓存的时间点不同,故加上过期时间后自然也不同)。spring.cache.redis.time-to-live = 600000

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

  • 读写加锁
  • 引入Canal中间件,感知到MySQL的更新去更新缓存
  • 读多写多,直接去数据库查询就行,不用添加缓存

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

特殊数据:特殊设计

posted @   佳沃  阅读(90)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示