zoukankan      html  css  js  c++  java
  • 双重检测机制解决缓存穿透问题

    什么是缓存穿透?

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

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

      //创建一个线程池
            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){ }   锁住了谁?

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

  • 相关阅读:
    PHP 函数
    PHP 循环
    PHP 数组
    PHP Switch 语句
    PHP If...Else 语句
    PHP 字符串
    PHP 变量
    用where导致group by分组字段的索引失效
    mysql之filesort原理
    Windows定时开机并开启工作软件
  • 原文地址:https://www.cnblogs.com/f-bob/p/14900180.html
Copyright © 2011-2022 走看看