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


    什么是缓存穿透?

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

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

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

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

  • 相关阅读:
    EntityFramework之领域驱动设计实践(二)
    博客园开始对X++语言语法高亮的支持
    一种来源于Microsoft Dynamics AX的权限管理设计思想
    使用InternalsVisibleToAttribute控制internal成员的访问
    EntityFramework之领域驱动设计实践:总结
    EntityFramework之领域驱动设计实践(九)
    EntityFramework之领域驱动设计实践(三)
    EntityFramework之领域驱动设计实践(七)
    EntityFramework之领域驱动设计实践(五)
    EntityFramework之领域驱动设计实践 (一)
  • 原文地址:https://www.cnblogs.com/f-bob/p/14900180.html
Copyright © 2020-2023  润新知