SpringCache是Spring提供的一个缓存框架,在Spring3.1版本开始支持将缓存添加到现有的spring应用程序中,在4.1开始,缓存已支持JSR-107注释和更多自定义的选项。
Spring Cache利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了,做到了对代码侵入性做小。
由于市面上的缓存工具实在太多,SpringCache框架还提供了CacheManager接口,可以实现降低对各种缓存框架的耦合。它不是具体的缓存实现,它只提供一整套的接口和代码规范、配置、注解等,用于整合各种缓存方案,比如Caffeine、Guava Cache、Ehcache。
我们这里使用常见的redis来缓存:
实现步骤:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>
spring:redis:host: 192.168.127.129port: 6379cache:type: redisredis:time-to-live: 3600000 # 设置所有缓存过期时间cache-null-values: true # 解决缓存null 也就是缓存穿透问题 可以不用key-prefix: CACHE # 设置缓存的前缀 可以不用use-key-prefix: true # 是否开启前缀的使用
//开启springcache缓存功能
@EnableCaching
经过以上的操作之后就可以使用缓存提供的常见注解来操作缓存
@Cacheable:将数据保存在缓存中 @CacheEvict:删除缓存数据 @CachePut:更新缓存 @Caching:组合以上多个操作 比如同时删除多个缓存 @CacheConfig:在类级别共享缓存的相同配置
注意:以上缓存使用中会有问题,如:缓存没有失效时间,缓存自动生成key,缓存数据是jdk序列化的数据,一般我们需要是json数据,所以需要自定义配置,缓存的key和存活时间都可以通过配置解决,但是缓存的序列化方式就需要自定义配置
自定义实现缓存的序列化方式为json:
//把CacheProperties注入容器
@EnableConfigurationProperties({CacheProperties.class})
@Configuration
public class MyCacheConfig {/*** 问题:配置文件中的配置没有用上* @return* CacheProperties cacheProperties容器中获取对象*/@BeanRedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();//修改k v的序列化机制config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));//配置文件中的所有配置生效CacheProperties.Redis redisProperties = cacheProperties.getRedis();if (redisProperties.getTimeToLive() != null) {config = config.entryTtl(redisProperties.getTimeToLive());}if (redisProperties.getKeyPrefix() != null) {config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());}if (!redisProperties.isCacheNullValues()) {config = config.disableCachingNullValues();}if (!redisProperties.isUseKeyPrefix()) {config = config.disableKeyPrefix();}return config;}
}
经过以上的配置之后,缓存的数据就是json格式
注意SpringCache的问题:
读模式:
1.缓存穿透:查询一个null数据库(缓存和数据库都没有), 设置mull
SpringCache是有解决的:spring.cache.redis.cache-null-values=true
2.缓存击穿:大量并发同时查询一个正好过期的数据(热点key) 加锁
SpringCache默认是无锁,但是@Cacheable可以解决 如下设置sync=true
3.缓存雪崩:大量的key同时过期 加随机时间
SpringCache是由解决:spring.cache.redis.time-to-live=过期时间 毫秒单
写模式(缓存和数据库一致):
1.读写加锁
2.引入Canal,感知mysql的binlog日志更新数据
3.读多写少,建议不适用缓存直接查询数据库就行
总结:常规数据(读多写少、即时性、一致性要求不高的数据)完全可以可以使用SpringCache