开始整合
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置
spring: redis: host: 192.168.100.10 port: 6379
spring.cache.type=redis spring.cache.redis.time-to-live=30000
常用注解
@Cacheable(value = {"category"},key = "'level1Categorys'")
验证
@Cachable注解
value指定分区,key值,不指定前缀那么就使用value分区
存在的问题
可以看到cache默认返回的不是json数据类型,默认使用的是java自带的解析器,因此我们需要将其修改为json格式的,这样保证跨平台数据传输。
在cache中默认自动进行了很多配置,这里是一个配置选择器,如果我们在配置文件中选择了一个缓存类型那么相关的配置就会加载进去。
在这个加载的Redis缓存配置文件中可以看到如果Redis配置是空那么就进行默认的配置,所以我们可以根据这个来进行自定义配置
在这个配置项中我们要开启CacheProperties去拿到Redis的配置文件里的配置。
@EnableConfigurationProperties(CacheProperties.class) @Configuration @EnableCaching public class MyCacheConfig { @Bean RedisCacheConfiguration cacheConfiguration(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; } }
@CacheEvict注解:
用于更新操作中,更新后将缓存中指定数据清除,也就是使用失效模式保证数据的一致性问题
@CacheEvict(value = {"category"},key = "'getLevel1Categorys'"),
@CacheEvict(value = {"category"},allEntries = true) //allEntries 开启后将category分区下的所有缓存数据都删除
@Caching
用于多个缓存注解联合操作
@Caching(evict = { @CacheEvict(value = {"category"},key = "'getLevel1Categorys'"), @CacheEvict(value = {"category"},key = "'getCatelogJson'") })
Cache和Redisson
Cache保证了缓存中有数据直接返回,如果缓存中没有数据开始加锁,执行查询数据库的业务,然后搭配@CacheEvict使用失效模式保证数据的一致性
@Cacheable(value = {"category"},key = "#root.methodName") @Override public Map<String, List<Catelog2VO>> getCatelogJson() { RLock lock = redisson.getLock("catelogJson-lock"); lock.lock(); Map<String, List<Catelog2VO>> dataFromDB = null; //加锁成功。。。执行业务 //设置过期时间,必须和加锁是同步的原子的 try { dataFromDB = getCatelogJsonFromDB(); } finally { lock.unlock(); } return dataFromDB; }
@Caching(evict = { @CacheEvict(value = {"category"},key = "'getLevel1Categorys'"), @CacheEvict(value = {"category"},key = "'getCatelogJson'") }) // @CacheEvict(value = {"category"},allEntries = true) @Override public void updateDetails(CategoryEntity category) { this.updateById(category); if (StringUtils.isNotEmpty(category.getName())) { categoryBrandRelationService.updateCategoryName(category.getCatId(), category.getName()); } }
不足
读模式:
- 缓存穿透:当查询某一个是空的数据,一直在缓存中查不到压力给到了数据库,解决方法:cache默认给空数据设置null,防止了缓存穿透
- 缓存雪崩:大量的key全部过期,解决方法:设置过期时间 spring.cache.redis.time-to-live=30000
- 缓存击穿:当某一个key过期的一瞬间大量请求同时进入。解决方法:加锁(Redisson或者使用cache自带的本地锁@Cacheable(value = {“category”},key = “#root.methodName”,sync = true))
写模式:
- 读写锁
- 引入Canal,感知数据库的更新去更新缓存
- 读多写少直接去数据库查
总结:
- 常规数据(读多写少、及时性、一致性要求不高的数据)可以使用cache
- 特殊数据:特殊设计