缓存穿透,缓存击穿,缓存雪崩区别
发现自己有时候明明已经做过了,可能是缺少回顾总结,过了几天这部分知识就会忘的一干二净,一点有记不住,还有这三个概念有时候确实不太能把他分清楚,面试的时候也有让自己说清楚,说的一塌糊涂,所以总结一下
一、缓存穿透,缓存击穿,缓存雪崩简单的区别
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会失效,请求都会打到数据库
缓存击穿问题也叫做热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问回给瞬间的数据库带来巨大的冲击
缓存雪崩是指同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
二、缓存穿透
缓存穿透常见的解决方案有两种:
1.缓存空对象
- 优点:实现简单,维护简单
- 缺点:额外的内存消耗,可能造成短期的不一致
2.布隆过滤
- 优点:内存占用较少,没有多余的key
- 缺点:实现复杂,存在误判的可能
3.解决缓存穿透的代码实现
public <R,ID> R queryWithPassThrough(
String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallBack,Long time ,TimeUnit unit){
//1.查询缓存
String key = keyPrefix + id;
String Json = stringRedisTemplate.opsForValue().get(key);
//2.判断缓存是否存在
if(StrUtil.isNotBlank(Json)){
//2.1 如果存在则直接返回数据给用户
return JSONUtil.toBean(Json,type);
}
if(Json != null){
return null;
}
//3.缓存不存在,查询数据库
R r = dbFallBack.apply(id);
//3.1如果数据库中也不存在,则返回错误信息
if(r == null){
//将一个“”空字符串直接存到redis中
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
return null;
}
//4.将数据库中的数据存入redis
this.set(key,r,time,unit);
//5.返回
return r;
}
三、缓存击穿
缓存击穿有两种常见的解决方法:
1.互斥锁
2.逻辑过期
3.两者的优缺点
4.互斥锁的代码实现
流程图
代码
public <R,ID>R queryWithMutex(
String keyprefix,String lockKeyPrefix,ID id,Class<R>type,Function<ID,R>dbFallBack,Long time ,TimeUnit unit) {
//1.查询缓存
String key = keyprefix + id;
String Json = stringRedisTemplate.opsForValue().get(key);
//2.判断缓存是否存在
if(StrUtil.isNotBlank(Json)){
//2.1 如果存在则直接返回数据给用户
return JSONUtil.toBean(Json,type);
}
if(Json != null){
//当缓存的内容不是空字符串""的时候
return null;
}
//2.2 缓存不是空字符串"",而是不存在时进行缓存重建
//3.缓存重建
//3.1获取互斥锁
String lockKey = lockKeyPrefix +id;
R r = null;
try {
boolean flag = tryLock(lockKey);
//3.2判断是否获取互斥锁成功
if(!flag){
//获取互斥锁失败
//3.3失败则休眠并且重试
Thread.sleep(50);
return queryWithMutex(keyprefix,lockKeyPrefix,id,type,dbFallBack,time,unit);
}
//3.4成功则进行查询数据库
//4.缓存不存在,查询数据库
r = dbFallBack.apply(id);
//模拟重建的延时
Thread.sleep(200);
//4.1如果数据库中也不存在,则返回错误信息
if(r == null){
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
return null;
}
//5.将数据库中的数据存入redis
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(r),time, unit);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//6.释放互斥锁
unLock(lockKey);
}
//7.返回
return r;
}
/**
* 开启锁
* @param key
* @return
*/
private boolean tryLock(String key){
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
/**
* 解锁
* @param key
*/
private void unLock(String key){
stringRedisTemplate.delete(key);
}
5.逻辑过期的代码实现
流程图
代码
public <R,ID>R queryWithLogicalExpire(
String keyprefix,String lockKeyPrefix,ID id,Class<R>type,Function<ID,R>dbFallBack,Long time ,TimeUnit unit){
//1.查询缓存
String key = keyprefix + id;
String Json = stringRedisTemplate.opsForValue().get(key);
//2.判断缓存是否为空
if(StrUtil.isBlank(Json)){
//Json为空,直接返回给用户null
return null;
}
//2.1命中,需要先把json反序列化为对象
RedisData redisData = JSONUtil.toBean(Json, RedisData.class);
R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
LocalDateTime expireTime = redisData.getExpireTime();
//2.2判断逻辑时间是否已经过期
if(expireTime.isAfter(LocalDateTime.now())){
//expireTime在当前时间之后,说明还没过期
return r;
}
//3当逻辑时间已经过期,需要缓存重建
//3.1获取互斥锁
String lockKey = lockKeyPrefix + id;
boolean flag = tryLock(lockKey);
//判断是否获取锁成功
if( flag){
//开启缓存重建,开启独立线程重建
CACHE_REBULID_EXECUTOR.submit(()->{
try {
//先查数据库
R r1 = dbFallBack.apply(id);
//2.封装逻辑过期时间
RedisData redisData1=new RedisData();
redisData1.setData(r1);
redisData1.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
//再写入Redis
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
//释放锁
unLock(lockKey);
}
});
}
//4.返回
return r;
}
/**
* 开启锁
* @param key
* @return
*/
private boolean tryLock(String key){
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
/**
* 解锁
* @param key
*/
private void unLock(String key){
stringRedisTemplate.delete(key);
}
标签:缓存,return,String,击穿,Json,雪崩,key,id
From: https://www.cnblogs.com/MyBlogZxw/p/17715981.html