soyo 2020-05-11
缓存穿透:用户恶意模拟请求很多缓存中不存在的数据,由于缓存中没有,数据库中也没有,大量的请求就会直接落在了数据库上,导致数据库出现异常
解决办法:
1.对接口进行限流 ,单个用户每分钟请求多少次
2.如果查询的数据为空,那么设置一个默认值到缓存中,这样第二次请求就会落到缓存中,而不会落到数据库;
//伪代码 public Object get(String key){ String value = redis.get(key); if(value==null || "".equals(value)){ //如果缓存为空,查询数据库 value = mysql.get(key); //如果数据库查询不到数据,也放入缓存,可以设置一个过期时间 if(value==null || "".equals(value)){ redis.set(key,value,3000); }else{ redis.set(key,value); } }else{ //如果缓存存在,直接返回 value = JsonUtils.toObject(value); } return value; }
缓存雪崩是指设置缓存时候设置了相同时间,缓存某一时刻全部失效,导致请求全部转发到数据库,数据库顺间压力过程导致雪崩(崩溃)
解决方案:
1.设置热点数据永不过期
2.缓存数据的过期时间设置为随机 ,这样过期的重复率就会降低
缓存击穿是缓存中没有数据但是数据库中有数据(一般是由于key过期导致),这时由于并发用户特别多,同时读取缓存没有读取到数据,同时去数据库读取数据 ,导致数据库压力瞬间增大
缓存击穿与缓存雪崩的区别是 击穿是针对某一个key,雪崩是针对很多key
解决方案:
1.对于热点的数据设置为永不过期
2.使用互斥锁,通常使用redis的setnx
public String get(String key){ //从缓存中获取数据 String value = redis.get(key); //如果缓存中没有数据 if(value == null){ //不要直接去数据库里取,设置一个锁,如果有一个人获取到了锁就去查询数据库然后添加缓存 //这里给锁设置三分钟避免del失败,下次缓存过期一直不能去数据库里取 if(redis.setnx(key_ex,1,3*60) == 1){ redis.set(key,value,expire); redis.del(key_ex); }else{ //没有获取到锁的用户,说明缓存中已经有人添加获取,直接获取即可 //休息50毫秒保险一点,因为redis添加需要时间 sleep(100); get(key); } } //缓存中有数据直接返回 return value; }