spring cache简化redis缓存开发

引入依赖

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

<!-- redis -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 <exclusions>
  <exclusion>
   <groupId>io.lettuce</groupId>
   <artifactId>lettuce-core</artifactId>
  </exclusion>
 </exclusions>
</dependency>

<!-- jedis -->
<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
</dependency>

写配置

spring:  
 redis:
  host: 192.168.56.10
  port: 6379
  password: 123456
 cache:
  type: redis

测试使用缓存

// 触发将数据保存到缓存的操作
@Cacheable: Triggers cache population.
// 触发将数据从缓存删除的操作
@CacheEvict: Triggers cache eviction.
// 不影响方法执行更新缓存
@CachePut: Updates the cache without interfering with the method execution.
// 组合以上多个操作
@Caching: Regroups multiple cache operations to be applied on a method.
// 在类级别共享缓存的相同配置
@CacheConfig: Shares some common cache-related settings at class-level.

1、启动类添加注解:@EnableCaching

2、方法添加注解:@Cacheable

/**
 * 每一个缓存的数据我们都来指定放到哪个名字的缓存【缓存的分区(按照业务类型分)】
 *
 * 代表当前方法的结果需要缓存,如果缓存中有,方法不用调用。
 * 如果缓存中没有,会调用方法,最后将方法的结果放入缓存
 * @return
 */

@Cacheable({"catagory"})
@Override
public List<CategoryEntity> getLevel1Categorys() {
	System.out.println("getLevel1Categorys......");
	long l = System.currentTimeMillis();
	return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
}

3、默认行为

  1. 如果缓存中有,方法不用调用
  2. key默认自动生成,缓存名字::SimpleKey [](自动生成的key)
  3. 缓存的value值,默认使用的是jdk的序列化机制,将序列化后的值存在redis中
  4. 默认时间ttl=-1

4、自定义属性

  1. 指定生成缓存的key:key属性指定接受spEL,@Cacheable(value={缓存名},key="‘key’"),由于spEL动态取值,所以需要再添加一个单引号,表达式语法可参考:https://docs.spring.io/spring-framework/docs/5.3.3-SNAPSHOT/reference/html/integration.html#cache-spel-context
  2. 指定缓存数据的存活时间:配置文件:spring.cache.redis.time-to-live: 300(单位毫秒)
  3. 将数据保存为json格式:
    • 原理:
      • CacheAutoConfiguration -> RedisCacheConfiguration -> 自动配置了RedisCacheManager -> 初始化所有的缓存 -> 每个缓存决定使用什么配置 -> 如果redisCacheConfiguration有就用已有的,没有就用默认配置 -> 想改缓存的配置,只需要给容器中放一个RedisCachcConfiguration即可 -> 就会应用到当前RcdisCacheManager管理的所有缓存分区中
    • 配置缓存信息:
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
    • 自定义缓存配置:
// 开启属性配置绑定
@EnableConfigurationProperties({
    CacheProperties.class
}
)
@EnableCaching
@Configuration
public class MyCacheConfig {
    /**
     * 配置文件中的东西没有用上
     *1、原来和配置文件绑定的配置类,是这样子的
     * @ConfigurationProperties(prefix = "spring.cache")
     * public class CacheProperties
     *2、要让他生效,要用这个注解,
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        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;
    }
}

级联数据的更新

1、失效模式:@CacheEvict

/**
 * @CacheEvict:失效模式
 * @param category
 */
@CacheEvict(value = {"catagory"}, key="'getLevel1Categorys'")
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {
    this.updateById(category);
    categoryBrandRelationDao.updateCategory(category.getCatId(), category.getName());
    //同时修改缓存中的数据,
    //redis.del('catalogJSON'),等待下次查询时更新
}
同时进行多种缓存操作:@Caching
@Caching(evict={
	@CacheEvict(value = {"catagory"}, key = "'getLevel1Categorys'"),
	@CacheEvict(value = {"catagory"}, key = "'getCatalogJson'")
})
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {
	// xxxx
}
或者指定删除某个分区下的所有数据@cacheEvict(value = “category” ,allEntries = true),存储同一类型的数据,都可以指定成同一个分区。分区名默认就是缓存的前缀:
@CacheEvict(value = {"catagory"}, allEntries = true)

2、双写模式:@CachePut

原理与不足

  1. 读模式

    • 缓存穿透:查询一个null数据;解决:spring.cache.redis.cache-null-values=true
    • 缓存击穿:大量并发进来查询一个正好过期的数据;解决:加锁:sync = true(该注解只能添加到@Cacheable且是本地锁)
    • 缓存雪崩:大量的key同时过期;解决:加随机时间:spring.cache.redis.time-to-live=3600000
  2. 写模式

    • 读写加锁
    • 引入canal,感知到mysql的更新去更新redis
    • 读多写多,直接查询数据库
  3. 原理

    • CacheManager(RedisCacheManager) -> cache(RedisCache) -> cache负责缓存的读写
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值