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 :传送门

  • 相关阅读:
    Spring 源码学习
    Feign Client 原理和使用
    算法基础:排序算法看这一篇就够了
    Spring 源码学习2
    Spring 源码学习
    最优包裹组合-贪心算法
    @Transactional 事务的底层原理
    Mysql索引扫盲总结
    snowflake原理解析
    分布式ID总结
  • 原文地址:https://www.cnblogs.com/geekdc/p/9256515.html
Copyright © 2011-2022 走看看