一、缓存穿透、缓存雪崩、缓存击穿
1、缓存穿透:查询一个不存在的数据
利用不存在的key请求网站,由于缓存服务器不命中。如果其一直请求,则每次都一直去查mysql数据库,这样就使我们的缓存服务器失去了意义。
每次都绕开redis缓存服务器而直接访问到MySQL服务器,在流量大的时候,db就直接挂掉。
利用不存在的key频繁攻击我们的应用,这样的漏洞。
解决方案:空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
pmsSkuInfo = getSkuByIdFromDb(skuId); //建立redis缓存服务器,就是为了保护这行代码:查询数据库的操作
if(pmsSkuInfo!=null) {
//mysql查询结果存入redis
jedis.set("sku:"+skuId+":info", JSON.toJSONString(pmsSkuInfo)); //把java对象 转为 json字符串
}else { //mysql查询结果为null,防止缓存穿透!!!!
//把这个不存在的skuId设为:空值json字符串null,并存到redis服务器中。
jedis.setex("sku:"+skuId+":info",60*3, JSON.toJSONString(""));
//seconds是秒。3分钟内再有请求这skuId,就直接返回一个空的对象给前端(没有任何信息)。不再执行查询数据库操作
}
2、缓存雪崩:雪崩是很多key集体失效
设置缓存时,采用了相同的过期时间,导致缓存某一时刻同时失效,请求全部怼到数据库,导致数据库压力过重雪崩
解决方案:设置不同的缓存失效时间,比如1-5分钟设置随机失效时间
3、缓存击穿:击穿是一个热点key失效
某个热点key在高并发访问的时候,突然失效(过期或者突发事件编译出错等),导致大量的并发请求怼到了mysql数据库的情况
解决方案:重点这里!采用分布式锁。
二、解决热点key缓存击穿的问题
(1)第1种分布式锁:redis自带一个分布式锁:set px nx
你的redis缓存服务器已经宕机了,在服务器重启过程。
就由其他的redis服务器的nx来实现分布式锁,限制用户数量访问数据库
//设置分布式锁,防止缓存击穿!!!
String ok = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 10);
if(StringUtils.isNotBlank(ok)&&ok.equals("ok")){
//设置锁返回ok成功,允许这位用户访问数据库,10秒后过期时间,再允许下一位访问
//这里是访问数据库的代码,省略。。。。看上一篇文档
}else { //设置锁不成功,自旋(设置该线程睡眠几秒,再重新尝试访问本方法)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getSkuById(skuId); //这里要return返回这个方法重新访问,同一条线程!!
//不加return,则创建一条新的线程,原来的线程还是访问不到,即原来的线程成了一条孤儿线程!
}
(2)第二种分布式锁:redisson框架,一个redis的带有juc的lock功能的客户端的实现(既有jedis的功能,又有juc的锁功能)
jedis在集群当中没有juc多线程锁的机制,单jedis是做不到同时访问redis服务器
juc锁的功能:实现用户1、2、3…等可以同时访问redis服务器
两种分布式锁的区别:
redisson框架的解决方法是 在客户端解决问题。
而redis自带的分布式锁是 在redis服务器解决问题。
三、方法自旋
错误的自旋代码。本来是A线程访问该访问,这里不加return,则创建新的一条B线程。由B线程重新访问本方法,A线程则成了孤儿线程!!!永远无法正常访问,这是java的基础知识了。
所以自旋本方法一定要加return!!!!