zoukankan      html  css  js  c++  java
  • 高并发下redis缓存穿透问题解决方案

    一、使用场景

      我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么redis缓存绝对是一个好的解决方案。

    二、需求

      假设有10000个请求,想达到第一次请求从数据库中获取,其他9999个请求从redis中获取这种效果。

    三、代码实现

    3.1、常规写法

    public List<UsersDO> getAllUserWithNoPage2(){
            try{
    
                //序列化器,将key的值设置为字符串
                RedisSerializer redisSerializer=new StringRedisSerializer();
                redisTemplate.setKeySerializer(redisSerializer);
    
                //查缓存
                List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
    
                if(null==list){
    
                    UsersQuery query=new UsersQuery();
                    list=usersDOMapper.selectByExample(query);
                    redisTemplate.opsForValue().set("allUsers", list);
                    System.out.println("从数据库中取数据");
                }
                else{
                    System.out.println("从缓存中取数据");
                }
                return list;
            }
            catch (Exception e) {
                logger.error("UserService.getAllUserWithNoPage error",e);
            }
            return null;
        }
    

      常规的这种写法单线程没有问题,但是考虑到并发的存在,就会出现缓存渗透的问题,也就是不能保证其他9999个请求都是从redis中取。

    3.2、常规写法压测

    @GetMapping(value = "/test2")
        public String  test2(){
            ExecutorService executorService= Executors.newFixedThreadPool(20);
    
            for(int i=1 ; i<=10000;i++){
    
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        userService.getAllUserWithNoPage2();
                    }
                });
            }
    
            return "test over";
        }
    

    3.3、常规写法压测结果

    3.4、常规写法的改进,使用双重检测锁

    public List<UsersDO> getAllUserWithNoPage(){
    
    
            try{
    
                //序列化器,将key的值设置为字符串
                RedisSerializer redisSerializer=new StringRedisSerializer();
                redisTemplate.setKeySerializer(redisSerializer);
    
                //查缓存
                List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
    
                if(null==list){
                    //双重检测 锁
                    synchronized (this) {
    
                        List<UsersDO> list1 = (List<UsersDO>) redisTemplate.opsForValue().get("allUsers");
                        if (null == list1) {
    
                            UsersQuery query=new UsersQuery();
                            list=usersDOMapper.selectByExample(query);
                            redisTemplate.opsForValue().set("allUsers", list);
    
                            System.out.println("从数据库中取数据");
                        }
                        else{
                            System.out.println("从缓存中取数据");
                        }
                    }
                }
                else{
                    System.out.println("从缓存中取数据");
                }
                return list;
            }
            catch (Exception e) {
                logger.error("UserService.getAllUserWithNoPage error",e);
            }
            return null;
        }
    

    3.5、双重检测锁压测

    @GetMapping(value = "/test")
        public String  test(){
            ExecutorService executorService= Executors.newFixedThreadPool(20);
    
            for(int i=1 ; i<=10000;i++){
    
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        userService.getAllUserWithNoPage();
                    }
                });
            }
    
            return "test over";
        }
    

    3.6、双重检测锁压测结果

    压测结果符合要求。

    完整代码已上传Github :传送门

  • 相关阅读:
    Python--文件操作
    Python--数据类型整理
    u-boot之NAND启动与NOR启动的区别
    u-boot之make all执行过程分析
    编译过程和符号表重定位问题、静态和动态链接
    u-boot之make <board_name>_config执行过程分析
    u-boot之ARM920T的start.S分析
    在使用Myeclipse时,用Tomcat添加部署项目的时候报错,或启动tomcat报错
    关于JDK,tomcat,eclipse的配置
    我的博客
  • 原文地址:https://www.cnblogs.com/geekdc/p/9256515.html
Copyright © 2011-2022 走看看