灵魂拷问:缓存为何被击穿! 何为击穿?
为何被击穿!
生活案例:相信"富有"的各位有过双十一和618抢商品的经历吧? 就酸没学过程序都咳哟粗略的估计一下,上百万人和自己抢东西是有多刺激.试想如果没有缓存机制。
上百万乃至上千万的同一时间请求查询,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。
总而言之,言而总之 就是 略过缓存 ! 简单来说各位程序流程都是自己写的,如果我们的缓存中没有了数据,我们的数据去哪里取?是的,数据库.准确来说,"我们"(热点秒杀商品上百万个同时请求在国内都很正常)直接进入请求了我们的数据库,那为什么会略过我的缓存呢?首先我们都明白一个redis的特性,key会过期。
何为击穿?
简明看一下key会具体出现的问题:
1、Key过期;
2、Key被页面置换淘汰。
对于第一个原因是因为在Redis中,Key有过期时间,如果某一个时刻(假如商城做活动,零点开始)key失效,那么零点之后对某一个商品查询请求将全都压到数据库上,导致数据库崩。
对于第二个原因,因为内存是有限的,要时时刻刻缓存新的数据,淘汰旧的数据,所以在一定的页面置换策略(常见页面置换算法)中,淘汰数据,如果某些商品做活动之前无人问津,势必会被淘汰。
上代码,以下是针对热点信息的方法块
@RequestMapping("getDetail")
public String getDetail(Modelmodel, Integerid) {
//互斥锁防止击穿
Goods goods = getGoodsByLock(id);
model.addAttribute("goods", goods);
return "sub/detail";
}
private Goods getGoodsByLock(Integerid) {
//1.先判断自己是否能获取到缓存的商品信息
Goods goods = (Goods) redisTemplate.opsForValue().get("goods" + id);
/**
*如果缓存中是空的, 不存在该对象的信息, 就去数据库中获取
*/
if (goods == null) {
//2.创建一个互斥锁,如果数据库中不存在互斥锁,添加一个唯一锁,注意:唯一锁也可以加上UUID,(MyConsts.GOODS_LOCK是自己写的静态变量)
Booleanflag = redisTemplate.opsForValue().setIfAbsent(MyConsts.GOODS_LOCK, 1);
//如果数据库没有锁,并且可以添加进去锁,证明可以进入数据库,便去mysql数据库中查询出当前对象存入缓存
if (flag == true) {
goods = goodsService.getGoods(id);
//将查询出的对象存入缓存,设置时长为20s如果20s内没人继续获取这个对象,防止缓存空间剧增,自动执行释放
redisTemplate.opsForValue().set("goods" + id, goods, 20,
TimeUnit.SECONDS);
//锁用完后释放掉
redisTemplate.delete(MyConsts.GOODS_LOCK);
} else {
//如果数据库中已经有锁,说明有人正在从数据库查询信息,考虑互斥性,主动为虚拟机(程序)制造2ms的间隔,等待已有锁消失
try {
Thread.sleep(2000);
//再次递归此方法,上锁后可执行添加和获取
getGoodsByLock(id);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
}
}
return goods;
}