Redisson解决Redis缓存存在的缓存穿透、缓存雪崩、缓存击穿

一、需求:

1、根据课程id查询发布的课程信息;

2、使用缓存提高系统白名单数据的访问效率,提高系统吞吐量;

二、基本功能实现:

1、功能代码:

public CoursePublish getCoursePublish(Long courseId) {
        return coursePublishMapper.selectById(courseId);
    }

2、功能测试效果:

3、压力测试:

基本功能的实现经过测试后我们可以看出,系统的效率并不是很高,只有88左右;

三、加入Redis缓存:

1、功能代码:

//注入redisTemplate
@Autowired
private RedisTemplate redisTemplate;

public CoursePublish getCoursePublish(Long courseId) {
        //从redis里面查询
        Object object = redisTemplate.opsForValue().get("course:" + courseId);
        if(object != null){
            //提取数据
            String objectString = object.toString();
        
            CoursePublish coursePublish = JSON.parseObject(objectString, CoursePublish.class);
            //返回课程信息
            return coursePublish;
        }else{
            //缓存中不存在,则从数据库中查询
            CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
            if(coursePublish == null){
                return null;
            }
            //数据存在则存入缓存
            String jsonString = JSON.toJSONString(coursePublish);
            redisTemplate.opsForValue().set("course:" + courseId,jsonString);
            //返回数据
            return coursePublish;
        }
    }

2、功能测试效果:

3、压力测试:

使用redis缓存后再使用jMeter压力测试,这时吞吐量直接到650+,系统效率显著提高。

四、使用reids缓存后可能出现的问题:

1、缓存穿透

(1)概念:我们在调用接口时查询一个数据库不存在的数据,导致请求仍然会指向数据库;

(2)解决思路:数据库查询不到返回的结果也是空,那就将null也存入到缓存中,由于课程不可能一直不存在,所以需要设置过期时间,避免结果错误;

(4)修改代码:

public CoursePublish getCoursePublish(Long courseId) {
        //从redis里面查询
        Object object = redisTemplate.opsForValue().get("course:" + courseId);
        if(object != null){
            //提取数据
            String objectString = object.toString();
            if(objectString.equals("null")){
                return null;
            }
            CoursePublish coursePublish = JSON.parseObject(objectString, CoursePublish.class);
            //返回课程信息
            return coursePublish;
        }else {
                //缓存中不存在,则从数据库中查询
                CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
                String jsonString = JSON.toJSONString(coursePublish);
                redisTemplate.opsForValue().set("course:" + courseId, jsonString, 30, TimeUnit.SECONDS);
                //返回数据
                return coursePublish;
            }
        }
    }

(5)压力测试:

吞吐量到690左右,稍有提高。

2、缓存雪崩

(1)概念:高并发的场景下,多个缓存同时过期,导致大量请求同时涌入数据库,使数据库压力陡增;

(2)解决思路:添加缓存时设置不同的过期时间;

(3)修改代码:

//缓存更新
redisTemplate.opsForValue().set("course:" + courseId, jsonString, 30 + new Random().nextInt(100), TimeUnit.SECONDS);

3、缓存击穿

(1)概念:高并发的场景下,大量数据同时访问一条即将过期的缓存,一旦缓存过期,大量请求同时会同时涌向数据库,数据库压力变大,系统效率也会降低;

(2)解决思路:

        1、本地使用同步锁,使得缓存更新只进行一次,同时在获得锁之后再查询一次缓存,避免后续请求获得锁后重复更新缓存

        2、多个微服务的情况下使用分布式锁(mysql乐观锁、redis的setNX、redisson),使得只有一个微服务更新缓存。我们这里模拟微服务场景下,使用redisson实现分布式锁。

(3)修改代码:

//注入Redisson
@Autowired
private RedissonClient redissonClient;

public CoursePublish getCoursePublish(Long courseId) {
        //从redis里面查询
        Object object = redisTemplate.opsForValue().get("course:" + courseId);
        if(object != null){
            //提取数据
            String objectString = object.toString();
            if(objectString.equals("null")){
                return null;
            }
            CoursePublish coursePublish = JSON.parseObject(objectString, CoursePublish.class);
            //返回课程信息
            return coursePublish;
        }else {
            //获取锁
            RLock lock = redissonClient.getLock("courseLock:" + courseId);
            lock.lock();
            try {
                //再次从redis里面查询
                object = redisTemplate.opsForValue().get("course:" + courseId);
                if(object != null) {
                    //提取数据
                    String objectString = object.toString();
                    if (objectString.equals("null")) {
                        return null;
                    }
                    CoursePublish coursePublish = JSON.parseObject(objectString, CoursePublish.class);
                    //返回课程信息
                    return coursePublish;
                }
                //缓存中不存在,则从数据库中查询
                CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
                String jsonString = JSON.toJSONString(coursePublish);
                redisTemplate.opsForValue().set("course:" + courseId, jsonString, 30 + new Random().nextInt(100), TimeUnit.SECONDS);
                //返回数据
                return coursePublish;
            }finally{
                //释放锁
                lock.unlock();
            }
        }
    }

(4)压力测试:

ok,吞吐稳定在600左右,性能提升问题解决。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值