Spring Boot集成Redis缓存之模拟高并发场景处理

前言

同样我们以上一篇文章为例子,搭建好环境之后,我欧美可以模拟高并发场景下,我们的缓存效率怎么样,到底能不能解决我们实际项目中的缓存问题。也就是如何解决缓存穿透?

Spring Boot集成Redis缓存高并发条件下处理

方式一、针数据量不是很大的情况下

之间加入同步锁synchronized、也能解决问题

@Override
public synchronized List<User> findAll() {
	业务省略.................
}

开始模拟Redis缓存高并发场景

UserController.java

 /***
     * 查询用户信息List集合 模拟用户高并发请求
     * @param model
     * @return
     */
    @GetMapping(value = "index")
    public String index(Model model){

        //线程、该线程调用底层所有查询用户信息的方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                userService.findAll();
            }
        };
        //多线程测试一下缓存穿透,模拟1万人同时访问
        ExecutorService executorService = Executors.newFixedThreadPool(25);
        for(int i=0 ; i<10000 ; i++){
            executorService.submit(runnable);
        }

        List<User> userList = userService.findAll();
        model.addAttribute("userList",userList);
        return "admin/index";
    }

我们先不加人同步锁synchronized看看缓存效果

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserDao userDao;

    //注入spring boot 自动配置好的RedisTemplate
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    
    @Override
    public List<User> findAll() {

        //1、设置key字符串的序列化器
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //2、查询Redis缓存
        List<User> userList = (List<User>) redisTemplate.opsForValue().get("allUsers");
        if(null == userList){
            System.out.println("查询数据库.......");
            //缓存为空,查询一遍数据库
            userList = userDao.findAll();
            //把查询出来的数据放入Redis中
            redisTemplate.opsForValue().set("allUsers",userList);
        }else{
            System.out.println("查询Redis中的缓存.......");
        }
        return userList;
    }
}

控制台打印

查询数据库.......
查询数据库.......
查询数据库.......
查询数据库.......
查询数据库.......
2020-05-11 19:32:48.114  INFO 12988 --- [ool-1-thread-25] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......

从中可看出一部分人请求了数据库、一部分请求了缓存、此时便出现了缓存穿透

方案一:这时加人同步锁synchronized再看看缓存效果

 //模拟高并发场景、加入synchronized同步锁
 @Override
 public synchronized List<User> findAll() {
     省略了,这部分代码和上面一样....
 }

加入synchronized同步锁、控制台打印

查询数据库.......
2020-05-11 19:37:44.877  INFO 7164 --- [pool-1-thread-1] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......
Java 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁,可以看出针数据量不是很大的情况下,加入synchronized同步锁能解决缓存穿透问题。那么问题来了,大数据量又怎么解决呢?

方式二:加入双重检测锁解决缓存穿透

UserServiceImpl.java,修改一下代码

//高并发条件下:出现缓存穿透、双重检测锁进行解决
    @Override
    public synchronized List<User> findAll() {

        //1、设置key字符串的序列化器
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //2、查询Redis缓存
        List<User> userList = (List<User>) redisTemplate.opsForValue().get("allUsers");
        if(null == userList){

            //加入锁进行锁定
            synchronized(this) {
                //加入锁,不同的人进来,先再从redis中获取以下,也就是说第一个进来了,后面的人再进来就可以直接读缓存中的数据了
                userList = (List<User>) redisTemplate.opsForValue().get("allUsers");
                //再进行判断
                if (null == userList) {
                    System.out.println("查询的数据库......");
                    //缓存为空,查询一遍数据库
                    userList = userDao.findAll();
                    //把查询出来的数据放入Redis中
                    redisTemplate.opsForValue().set("allUsers", userList);
                } else {
                    System.out.println("进来了、还是查询Redis中的缓存.......");
                }
            }

        }else{
            System.out.println("查询Redis中的缓存.......");
        }
        return userList;
    }

控制台输出

查询的数据库......
2020-05-11 19:48:01.976  INFO 9396 --- [pool-1-thread-2] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
查询Redis中的缓存.......
查询Redis中的缓存.......
查询Redis中的缓存.......

我们将并发数量再改大一点 比如10万人同时访问,也是没问题的

你可能感兴趣的:(SpringBoot系列,redis,缓存,sync)