双重检测机制解决缓存穿透问题

什么是缓存穿透?

当大量并发访问时,首批并发会在没有查询到缓存的情况下集体访问数据库,造成缓存暂时性无效。

话不多说,直接上代码,先创建一个线程池

 

  //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(4 * 2);

        for (int i=0; i<5000; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    studentService.getStudentById(1);
                }
            });
        }

  

调用方法

public Student getStudentById(Integer id) {

        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //查询缓存
        Student student = (Student) redisTemplate.opsForValue().get("studentKey");

        //判断缓存是否为空
        if (null == student) {

                    System.out.println("查询了数据库......");
                    //查询数据库
                    student = studentMapper.selectByPrimaryKey(id);
                    //放入缓存
                    redisTemplate.opsForValue().set("studentKey", student);
        } else {
            System.out.println("查询了缓存......");
        }
        return student;
    }

  

结果:

 

 

显而易见,在第一批并发下的第一个查询还没存入redis的时候,后面几个线程已经去找数据库的要完数据了。如果第一批并发体量很大,数据库就有可能崩溃。

怎么解决哪?

 

 

第一个解决方案:

在方法上加synchronized,让他们排队访问。

 

运行输出:

 

 

这个解决方案是有效的,但是这个解决方案存在明显的弊端,效率慢到姥姥家了。5w个并发请求,跑了好几分钟。

 

尝试第二个解决方案:

  public /*synchronized*/ Student getStudentById(Integer id) {

        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //查询缓存
        Student student = (Student) redisTemplate.opsForValue().get("studentKey");

        //判断缓存是否为空
        if (null == student) {

            //双重检测锁实现
            synchronized (this) {
                
                student = (Student) redisTemplate.opsForValue().get("studentKey");

                if (null == student) {
                    System.out.println("查询了数据库......");
                    //查询数据库
                    student = studentMapper.selectByPrimaryKey(id);
                    //放入缓存
                    redisTemplate.opsForValue().set("studentKey", student);
                }
            }

        } else {
            System.out.println("查询了缓存......");
        }
        return student;
    }

  

运行结果:

 

 

效率大大的提升上来了,这就是双重检测机制,那么问题来了。

 

问问各位小伙伴:

这个synchornzied(this){ }   锁住了谁?

如果把第二次缓存查询去掉,结果会怎样?

 

posted @ 2021-06-18 16:45  f_Bob  阅读(246)  评论(1编辑  收藏  举报