Redis高并发常见问题:缓存穿透/缓存雪崩/缓存击穿的概念及解决方案

1.缓存穿透

概念

缓存穿透就是访问数据库中不存在的数据,高并发情况下或有人恶意的不停的访问该数据,导致请求打到数据库,直至数据库崩溃.
比如我们在写项目的时候,商品的主键id很少为负数.那么可能有人就会不停的访问id为-1的商品.而这个商品又不存在,导致我们的数据库崩溃.

解决方案

这里我通常的解决方案是在一个请求打到数据库返回null值时,在redis中给这个商品的key存入一个empty数据.使后边的请求直接访问redis而不是再去访问数据库.

  jedis.setex(key,60,"empty");

但是这并不是绝对安全的解决方案,如果你想了解更多,可以查看我的另一篇文章布隆过滤器

2.缓存雪崩

概念

缓存雪崩就是我们在设置缓存数据时,设置的有效期相同,导致在同一时刻,大部分的缓存同时到期,这时候,所有的请求在redis中拿不到数据,都去访问数据库,数据库就受到了所有请求的压力,在高并发的情况下,成千上万个请求同时到达数据库,导致数据库崩溃.

解决方案

对于缓存雪崩,我们通过概念可以发现,之所以会雪崩,就是因为多个缓存同时到期,那我们让他不同时到期就可以了,实现方式就是利用setex命令和Random类生成随机数的方式为缓存设置随机有效时间.这样就基本上不会出现多个缓存同时到期的现象了,也就是基本上不会出现缓存雪崩的现象了.

  int random=new Random().nextInt(60*10);
   jedis.setex(key,random,"empty");

3.缓存击穿

概念

缓存击穿是缓存穿透和缓存雪崩的结合,意思是一个数据太热点,在高并发情况下,这个数据的缓存到期,成千上万的请求又绕过了redis直接打到了数据库,导致数据库的崩溃.

解决方案

分布式锁.
分布式锁就是一把限制人流的锁,像一个保安一样,守护着进入数据库的入口.对于抢到锁的那一个请求才会放行,后面的请求只能排队.等待抢到锁的人进入数据库将数据从数据库取出来存到缓存中,才允许后面的人进来访问,此时访问的时候,前面的人已经将数据存放到缓存中,则后面的人就可以从Redis中取数据而不是直接访问数据库了.
关于分布式锁的具体实现方式及解释请参考我的这篇文章

缓存击穿,缓存雪崩与缓存穿透的关系

缓存击穿和缓存雪崩都是缓存穿透,只不过是它的特殊表现形式

#.处理三个问题的完整代码

    public Student selectStudentById(Integer id) {
    String key="student-20-10-2:"+id+"-key";
        String lockKey=key+":lock-"+id;
        String value= UUID.randomUUID().toString();
        Jedis jedis = redisUtils.getJedis();

        String s = jedis.get(key);
        Student student=null;
        if (s==null){
            System.out.println("key:["+key+"]访问数据库获取数据");
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock(50, TimeUnit.SECONDS);
            /* String set = jedis.set(lockKey, value, "NX", "EX", 50);*/
            if (lock.isLocked()){
                student = studentMapper.selectByPrimaryKey(id);
                int random=new Random().nextInt(60*10);
                if (student==null){
                    jedis.setex(key,random,"empty");
                }else{
                    jedis.setex(key,random,JSON.toJSONString(student));
                }
/*                if (jedis.get(lockKey).equals(value)){
                    jedis.del(lockKey);
                }*/
                lock.unlock();
            }else{
                /*没有抢到锁,进行回调*/
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return selectStudentById(id);
            }


        }else{
            System.out.println("key:["+key+"]从Redis中获取数据");
            if (s.equals("empty")) {
                student=null;
            }else{
                student = JSON.parseObject(s, Student.class);
            }

        }


        return student;
       }

你可能感兴趣的:(Java,缓存,redis,缓存,java,分布式)