灵魂拷问:缓存为何被击穿! 何为击穿?

             为何被击穿!

      生活案例:相信"富有"的各位有过双十一和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;
}