原创

SpringBoot集成Redis以及解决高并发问题

工程结构图:

提前说明:

注意:JavaBean必须实现序列化,不然数据无法存入Redis。
public class User implements Serializable

1.SpringBoot集成Redis实现缓存

首先需要引入SpringBoot的Redis依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

关键代码:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    /**注入SpringBoot自动配置好的redisTemplate**/
    //注意:spring boot帮我们注入的redisTemplate类,泛型里面只能写 <String, String>、<Object, Object>,不能一个String一个Object!
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Override
    public List<User> findAllUser() {

        //字符串的序列化器(这里只将key序列化--->AllUser),为了在Redis管理工具更好看一点(AllUser不会出现前面的类似乱码的)
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //查询缓存
        List<User> userList = (List<User>) redisTemplate.opsForValue().get("AllUser");
        if (userList == null){
            //缓存为空,查询数据库
            userList = userMapper.findAllUser();
            //把查询出来的数据放入Redis中
            redisTemplate.opsForValue().set("AllUser",userList);
        }

        return userList;
    }

    @Override
    public User findUserById(Integer id) {

        //字符串的序列化器(这里只将key序列化--->AllUser),为了在Redis管理工具更好看一点(AllUser不会出现前面的类似乱码的)
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //查询缓存
        User user = (User) redisTemplate.opsForValue().get("User");
        if (user == null){
            //缓存为空,查询数据库
            user = userMapper.findUserById(id);
            //把查询出来的数据放入到Redis中
            redisTemplate.opsForValue().set("User",user);
        }

        return user;
    }
}

总结思路:

1.字符串序列化(名字)。
2.查询Redis缓存,如果为空,查询数据库。
3.将查询出来的数据放入Redis缓存中。
4.返回结果。

思考:

如果一万个人同时访问(高并发条件下),上面的代码是否有什么不恰当?

2.高并发下SpringBoot集成Redis缓存的问题

关键代码:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    /**注入SpringBoot自动配置好的redisTemplate**/
    //注意:spring boot帮我们注入的redisTemplate类,泛型里面只能写 <String, String>、<Object, Object>,不能一个String一个Object!
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    /**
     * 测试高并发问题
     * @param name
     * @return
     */
    @Override
    public List<User> findUserByName(String name) {

        //字符串的序列化器(这里只将key序列化--->AllUser),为了在Redis管理工具更好看一点(AllUser不会出现前面的类似乱码的)
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        List<User> userList = (List<User>) redisTemplate.opsForValue().get("findByLikeName");

        if (userList == null){
            synchronized (this){
                //查询Redis缓存
                userList = (List<User>) redisTemplate.opsForValue().get("findByLikeName");
                if (userList == null){
                    //查询数据库
                    userMapper.findUserByName(name);
                    System.out.println("查询的数据库...");
                    //将查询的结果放入Redis缓存中
                    redisTemplate.opsForValue().set("findUserByName",userList);
                }else {
                    System.out.println("查询的缓存...");
                }
            }
            System.out.println("查询的缓存...");
        }

        return userList;
    }
}

Controller:

@Controller
public class UserController {

    @Autowired
    private UserService userService;


    /**
     * 高并发问题
     * @param name
     * @return
     */
    @RequestMapping("/findUserByName/{name}")
    @ResponseBody
    public List<User> findUserByName(@PathVariable("name") String name){

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                userService.findUserByName(name);
            }
        };
        ExecutorService executorService = Executors.newFixedThreadPool(25);
        for (int i = 0; i < 10000; i++) {
            executorService.submit(runnable);
        }

        List<User> userList = userService.findUserByName(name);
        return userList;
    }
}

效果图:

可以看到查询数据的次数非常非常多,这样对数据库的压力十分大,甚至我查询不出结果!

3.解决高并发问题

关键代码:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    /**注入SpringBoot自动配置好的redisTemplate**/
    //注意:spring boot帮我们注入的redisTemplate类,泛型里面只能写 <String, String>、<Object, Object>,不能一个String一个Object!
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    /**
     * 测试解决高并发问题
     * @param id
     * @return
     */
    @Override
    public User findUserById(Integer id) {

        //字符串的序列化器(这里只将key序列化--->AllUser),为了在Redis管理工具更好看一点(AllUser不会出现前面的类似乱码的)
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //查询Redis缓存
        User user = (User) redisTemplate.opsForValue().get("User");

        //解决高并发问题:双重监测锁
        if (user == null){

            synchronized (this){

                //查询Redis缓存
                user = (User) redisTemplate.opsForValue().get("User");

                if (user == null){
                    //缓存为空,查询数据库
                    user = userMapper.findUserById(id);
                    System.out.println("查询的数据库...");

                    //把查询出来的数据放入到Redis中
                    redisTemplate.opsForValue().set("findUserById",user);
                }else {
                    System.out.println("查询的缓存...");
                }
            }
        }else {
            System.out.println("查询的缓存...");
        }

        return user;
    }


}

Controller:

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 测试解决高并发问题
     * @param id
     * @return
     */
    @RequestMapping("/findUserById/{id}")
    @ResponseBody
    public User findUserById(@PathVariable("id") Integer id){

        //多线程,该线程调用底层根据id查询学生的方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                userService.findUserById(id);
            }
        };
        //多线程测试高并发条件下内网穿透问题
        ExecutorService executorService = Executors.newFixedThreadPool(25); //线程池根据CPU来选择适当的值
        for (int i = 0; i < 10000; i++) {
            //提交线程runnable方法
            executorService.submit(runnable);
        }

        User user = userService.findUserById(id);
        return user;
    }
}

效果图:

或者配置好Druid数据源:

发现只查询了一次数据库!

正文到此结束
本文目录