SpringBoot集成redis

本文详细介绍了如何在SpringBoot项目中集成Redis,包括在pom文件中添加依赖,配置Redis连接信息,使用RedisTemplate操作Redis,以及防止内存穿透的策略。通过在业务逻辑中实现缓存数据并设置过期时间,有效缓解数据库压力。
摘要由CSDN通过智能技术生成

SpringBoot集成redis

Redis

特点:

  • 基于key-value进行存储;
  • 支持多种数据结构:string(字符串); list(集合); hash(哈希); set(集合); zset(有序集合)
  • 支持持久化,通过内存进行存储,也可以存到硬盘里面
  • 支持过期时间,支持事务
  • 使用场景,一般来讲,把经常进行查询,不经常修改,不是特别重要的数据放到redis中作为缓存,比如首页的一些数据,但是例如财务数据这些重要的数据,不能放在redis中做缓存。

一、集成redis

1、在pom中导入相关依赖
 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
2、在properties或yml文件中配置redis连接信息

yml文件引入格式

spring:
  redis:
    database: 0 #Redis数据库索引(默认为0)
    host: 127.0.0.1 #Redis服务器地址
    port: 6379 #Redis服务器连接端口
    password:  #Redis服务器连接密码(默认为空)
3、在业务中(servcieImpl)引入RedisTemplate
@Autowired
    private RedisTemplate<Object,Object> redisTemplate;
4、将查询到的信息保存到redis中
 //查询全部组别的方法
    @Override
    public List<Groups> selectAll() {
        //查询的时候直接查询缓存中的Groups数据
        List<Groups> groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
        //判断缓存中有没有Groups
        if(null == groups){
            //如果没有就查询数据库
            groups = groupsMapper.selectAll();
            //然后将查询回的数据放在缓存中,下一个查询者就会从缓存获取Groups
            redisTemplate.opsForValue().set("allGroups",groups);
        }
        return groups;
    }
5、将redis中存入的key值,变成可读字符串
 //将redis中存入的key值,变成可读字符串
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

整体如下

    //查询全部组别的方法
    @Override
    public List<Groups> selectAll() {
        //将redis中存入的key值,变成可读字符串
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        //查询的时候直接查询缓存中的Groups数据
        List<Groups> groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
        //判断缓存中有没有Groups
        if(null == groups){
            //如果没有就查询数据库
            groups = groupsMapper.selectAll();
            //然后将查询回的数据放在缓存中,下一个查询者就会从缓存获取Groups
            redisTemplate.opsForValue().set("allGroups",groups);
        }
        return groups;
    }

注意: 使用redis缓存实体信息,实体类必须被序列化(实现Serializable) 接口

二、redis的内存穿透

redis解决的问题就是减轻数据库的压力,让数据库的内容保存到redis中,但是在并发条件下判断条件没有起到拦截的效果,第一次所有的请求依然会请求到数据库,虽然redis有数据。

三、内存穿透的解决措施

给查询方法加上synchronized锁

 //判断缓存中有没有Groups
        if(null == groups){
            //加锁
            synchronized (this){
                //如果缓存中有数据,就查询缓存
                groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
                if(null == groups){
                    //如果没有就查询数据库
                    groups = groupsMapper.selectAll();
                    //然后将查询回的数据放在缓存中,下一个查询者就会从缓存获取Groups
                    redisTemplate.opsForValue().set("allGroups",groups);
                    }
                }
            }

最后serviceimpl代码如下

 //查询全部组别的方法
    @Override
    public List<Groups> selectAll() {
        //将redis中存入的key值,变成可读字符串
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        //查询的时候直接查询缓存中的Groups数据
        List<Groups> groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
        //判断缓存中有没有Groups
        if(null == groups){
            //加锁
            synchronized (this){
                //如果缓存中有数据,就查询缓存
                groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
                if(null == groups){
                    //如果没有就查询数据库
                    groups = groupsMapper.selectAll();
                    //然后将查询回的数据放在缓存中,下一个查询者就会从缓存获取Groups
                    redisTemplate.opsForValue().set("allGroups",groups);
                    }
                }
            }

        return groups;
    }

四、测试

在controller中测试这个方法

@GetMapping("selectAll")
    @ApiOperation(value = "查询全部即时组单子")
    public R selectAll(){
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        for(int i = 0;i<10000;i++){
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    groupsService.selectAll();
                }
            });
        }
        return R.ok(groupsService.selectAll());
    }

在方法中,创建一个线程池,实现Runnable接口,重写run方法,模拟10000个用户同时访问,调用service中的selectAll方法

public List<Groups> selectAll() {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        List<Groups> groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
        if(null == groups){
            synchronized (this){
                groups = (List<Groups>) redisTemplate.opsForValue().get("allGroups");
                if(null == groups){
                    groups = groupsMapper.selectAll();                    
                    redisTemplate.opsForValue().set("allGroups",groups);
                    }
                }
            }
        return groups;
    }

来说一下大概流程,当controller调用service中的selectAll方法时,先去查询缓存返回一个groups集合,然后到达第一层if判断,此时groups为空,10000个访问全部进入第一个if判断,此时遇到synchronized锁,锁只让第一个访问进入,再一次查询redis,然后进行第二个if判断,发现redis返回还是空,然后就查询数据库,返回了数据groups,然后将groups放在redis中,之后的9999个访问就直接查询redis了,这样就达到了减轻数据库压力的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值