缓存击穿

问题1:当数据没有缓存的时候,正好有很多个请求一起去访问数据。

解决方法1:

利用setnx增加分布式锁

同时只有一个线程能拿到锁,其他线程自旋等待
问题2:如果某个线程拿到锁很长时间没处理完,其他线程会一直等待。

问题3:如果设置锁的超时时间,在释放锁del的时候,可能删除的是别人所持到的锁。

解决方法,得到锁的线程只会释放自己的锁 每个线程生成一个一个随机token,释放锁的时候加以验证

缓存穿透

查询一个不存在于缓存和数据库的数据,永远不会加载缓存,会反复查询数据库,增加io压力

 @Override
    public SkuInfo getSkuById(String skuId) {
        //redis缓存和缓存锁应该为不同的节点,这里简化为在同一个节点上

        Jedis jedis = redisUtil.getJedis();
        SkuInfo skuInfo = null;

        //查询redis缓存
        String key = "sku:" + skuId + ":info";
        String val = jedis.get(key);
        //此数据在数据库中也没有,直接返回空值
        //避免缓存穿透,即数据库和缓存中都没有值,却不断的去查询,导致宕机
        if ("empty".equals(val)) {
            return skuInfo;
        }

        if (StringUtils.isBlank(val)) {//没有命中缓存,查询数据库

            //申请缓存锁
            String OK = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 3000);

            if ("OK".equals(OK)) {//拿到缓存锁
                //查询数db
                skuInfo = getSkuByIdFormDb(skuId);

                if (skuInfo != null) {
                    //同步缓存
                    jedis.set(key, JSON.toJSONString(skuInfo));
                } else {
                    //当其他线程再执行时,通知同伴,数据库中没有
                    jedis.setex("sku:" + skuId + ":info", 10, "empty");
                }
                //归还锁
                jedis.del("sku:" + skuId + ":lock");

            } else {//没有拿到缓存锁
                //自旋
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                getSkuById(skuId);

            }
        } else {//查询的数据在缓存中
            skuInfo = JSON.parseObject(val, SkuInfo.class);
        }
        return skuInfo;
    }