正常情况下,我们去查询数据大部分都是存在的。如果请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去,造成对后端数据库的较大压力。这种查询不存在数据的现象我们称为缓存穿透。(有可能会是某些不法份子的恶意行为,多线程打满去向服务查询不存在的数据)
解决办法
在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。
比如:鹿晗宣布恋情,导致微博瘫痪。就有可能是缓存击穿导致的,大家都去看这一个热点新闻,热点新闻的缓存如果超时失效了,就造成后端服务压力增大,服务器瘫痪。(当然这只是我猜的,举例而已)
解决办法
可以通过准确的监控热点流量,及时的针对热点服务及缓存组件进行自动化的扩容。
通过Hystrix或sentinel等服务限流工具,保证系统的可用性,拒绝掉一部分流量的访问。
第三种方法就是加锁,SpringCache采用sync
属性,只有一个线程去维护缓存,其他线程会被阻塞,直到缓存中更新该条目为止。也就是第一次查询只允许一个线程,等数据被缓存之后,才支持并发。
@Cacheable(value = CACHE_OBJECT,key = “#id”,sync=true)
public ArticleVO getArticle(Long id) {
同一时刻大量缓存失效,导致请求集中的全部打到数据库。比如:双十一零点搞活动,为了支撑这次活动,事先已经缓存好大量的数据。如果所有的数据全是缓存24小时,那24小时之后这些数据缓存将集中失效,最终结果就是11.12号服务崩溃。
解决办法
在 application.yml指定 spring.cache.type=redis。
spring:
cache:
type: redis
redis:
cache-null-values: true # 缓存null,防止缓存穿透
use-key-prefix: true # 是否使用缓存前缀
key-prefix: boot-launch # 缓存前缀,缓存按应用分类
time-to-live: 3600 # 缓存到期时间,默认不主动删除永远不到期
其中值得注意的一点是,Spring Cache默认只支持全局对所有的缓存配置生效时间,不支持对缓存的生效时间分类配置,容易造成缓存雪崩。
由于redis缓存设置的到期时间是统一的,没有办法根据缓存名称(value属性)分别设置缓存到期的时间,容易造成缓存雪崩。所以我们进行一个简单的改造。在改造之前我们先来看一下RedisCacheManager源码
本专栏是一个连贯的系列专栏,如果存在看不懂的代码,请在本专栏中按自然排序查看之前的2-3篇文章。
RedisCacheManager构造函数包含三个参数
RedisCacheWriter
这个在之前的章节我们就配置过RedisCacheConfiguration defaultCacheConfiguration
这个是默认的全局配置,针对所有缓存Map initialCacheConfigurations
这个是针对某一种缓存的个性化配置,泛型String是缓存名称,泛型RedisCacheConfiguration是该缓存的个性化配置理解了上面的源码,下面的改造代码就不难理解了。
@Data
@Configuration
@ConfigurationProperties(prefix = "caching") //application.yml配置前缀
public class RedisConfig {
//11.4章节代码,不是本节内容
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//序列化重点在这四行代码
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
//从这里开始改造
//自定义redisCacheManager
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
this.buildRedisCacheConfigurationWithTTL(redisTemplate,RedisCacheConfiguration.defaultCacheConfig().getTtl().getSeconds()), //默认的redis缓存配置
this.getRedisCacheConfigurationMap(redisTemplate)); //针对每一个cache做个性化缓存配置
return redisCacheManager;
}
//配置注入,key是缓存名称,value是缓存有效期
private Map ttlmap; //lombok提供getset方法
//根据ttlmap的属性装配结果,个性化RedisCacheConfiguration
private Map getRedisCacheConfigurationMap(RedisTemplate redisTemplate) {
Map redisCacheConfigurationMap = new HashMap<>();
for(Map.Entry entry : ttlmap.entrySet()){
String cacheName = entry.getKey();
Long ttl = entry.getValue();
redisCacheConfigurationMap.put(cacheName,this.buildRedisCacheConfigurationWithTTL(redisTemplate,ttl));
}
return redisCacheConfigurationMap;
}
//根据传参构建缓存配置
private RedisCacheConfiguration buildRedisCacheConfigurationWithTTL(RedisTemplate redisTemplate,Long ttl){
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
.entryTtl(Duration.ofSeconds(ttl));
}
}
在 application.yml指定 缓存名称对应的缓存生效时间,单位为秒
caching:
ttlmap:
article: 10
xxx: 20
yyy: 50
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦