大众点评项目 缓存穿透、缓存击穿、缓存雪崩
- 需求:缓存穿透、缓存击穿、缓存雪崩
- 处理策略
- 缓存穿透处理
- 缓存雪崩
- 缓存击穿
- 总结
SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则
- 基础+实战的Demo和Coding上传到我的代码仓库
- 在原有基础上加入一些设计模式,stream+lamdba等新的糖
- 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式
代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisThreeStrategiesProject
需求:缓存穿透、缓存击穿、缓存雪崩
处理策略
缓存穿透处理
- 布隆过滤器:本质上是一个概率运算器,可以将存储的数据分解成二进制数据,来进行推测传入数据是否存在,有一定不确定性,但是如果判断为不存在,则一定不存在;
- 添加Null值
//处理缓存穿透: id范围就在0-10000
if (id <= 0 || id> 10000) {
return Result.fail("店铺类型不存在!");
}
添加Null值
if (shop == null) {
//处理缓存穿透
stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
return Result.fail("店铺类型不存在!");
}
判断Null值
//处理缓存穿透: 如果为空值
if (shopStr != null){
return Result.fail("店铺类型不存在!");
}
缓存雪崩
- 可以设置随机时间
- 设置异地多活,主从架构等方式
//处理缓存雪崩: 随机时间 time * RedisConstants.CACHE_NULL_TTL
Random random = new Random();
Long time = random.nextLong();
stringRedisTemplate.opsForValue().set(key, "", time * RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
缓存击穿
如果当前热点信息失效,大量访问就落在DB上,我们可以通过
- 互斥锁(setnx)
- 逻辑过期机制
//互斥锁处理缓存击穿
String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
try {
if (!tryLock(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS))) {
Thread.sleep(50);
return queryById(id);
}
log.debug("lockKey" + lockKey);
shop = getById(id);
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_NULL_TTL, TimeUnit.HOURS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
/*e.printStackTrace();*/
}finally {
//删除暂时的key, 释放互斥锁
stringRedisTemplate.delete(lockKey);
}
所有代码
@Override
public Result queryById(Long id) {
//处理缓存穿透: id范围就在0-10000
if (id <= 0 || id> 10000) {
return Result.fail("店铺类型不存在!");
}
String key = RedisConstants.CACHE_SHOP_KEY + id;
String shopStr = stringRedisTemplate.opsForValue().get(key);
if(StrUtil.isNotBlank(shopStr)){
Shop shop = JSONUtil.toBean(shopStr, Shop.class);
return Result.ok(shop);
}
//处理缓存穿透: 如果为空值
if (shopStr != null){
return Result.fail("店铺类型不存在!");
}
//处理缓存雪崩: 随机时间 time * RedisConstants.CACHE_NULL_TTL
Random random = new Random();
Long time = random.nextLong();
Shop shop = null;
//互斥锁处理缓存击穿
String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
try {
if (!tryLock(lockKey)) {
Thread.sleep(50);
return queryById(id);
}
log.debug("lockKey" + lockKey);
shop = getById(id);
if (shop == null) {
//处理缓存穿透
stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
return Result.fail("店铺类型不存在!");
}
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_NULL_TTL, TimeUnit.HOURS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
/*e.printStackTrace();*/
}finally {
//删除暂时的key, 释放互斥锁
stringRedisTemplate.delete(lockKey);
}
return Result.ok(shop);
}
public boolean tryLock(String key){
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS);
//这里不可以直接返回flag,因为在拆箱过程中可能出现flag为null的情况;
return BooleanUtil.isTrue(flag);
}
总结