redis本地代码实战(3)模拟缓存击穿,使用互斥锁实现

本文介绍了缓存击穿的概念,即热点key失效导致大量请求冲击数据库的问题。通过使用互斥锁,确保同一时间只有一个线程访问数据库,避免并发请求引发的数据库压力。文章详细描述了处理流程和代码实现,包括使用ReentrantLock进行锁管理以及如何在数据未命中时缓存空值策略。
摘要由CSDN通过智能技术生成

缓存击穿概念

缓存击穿也可以理解成热点key问题,顾名思义就是存在一个被频繁访问的热点key数据,但是这个key突然过期或者由于其他原因失效了,并且此时针对于此key的新的缓存还未建立完成,于是所有的请求直接全部打到了数据库中,导致数据库压力激增,存在宕机风险

互斥锁

互斥锁保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。最后给锁也添加过期时间。

流程大概是这样

首先我们假设有两个线程A,B同时访问服务请求一个key的数据,线程A进入查询流程,首先会去从缓存中获取数据,如果此时没有从缓存中拿取到对应的数据,会尝试去获取互斥锁,获取到互斥锁之后才会去建立数据库查询去访问数据库获取数据,获取到之后再写入缓存中,最后再释放锁。当线程A还在查询流程中的时候,此时线程B进入查询流程,也是一样先尝试从缓存中获取数据,如果没有查询出来,就尝试去获取互斥锁从数据库中查询,而此时由于线程A已经占有了互斥锁,而互斥锁只允许同一时间只有一个线程在操作,所以一般流程下,线程B会进入一段休眠,等休眠时间过去之后再去尝试获取互斥锁,进入一个休眠自旋的状态,直到获取到互斥锁为止,如果在自旋状态中缓存中已经被写入了这个key的值,那么线程B则会在自旋的第一步就直接从缓存中读取到值然后直接返回了。


代码实现

//缓存击穿实现
    private String redisTest1(String userName) throws Exception {
        //首先请求进来,直接从redis查询查看是否存在
        String redisResult = stringRedisTemplate.opsForValue().get("stefentest");
        //判断redis中是否存在
        if(redisResult != null){
            //如果存在,就直接返回
            return "redis查询成功,result结果为"+redisResult;
        }
        //如果redis中数据未命中,则构建数据库查询
        ReentrantLock lock = new ReentrantLock();
        try{
            lock.tryLock();
            if(lock.tryLock()){
                System.out.println("上锁,不允许其他线程构建数据库查询");
                String localResult = this.selectByUsername(userName);
                if(localResult != null){
                    //如果数据库中也没有查找到,此时就需要我们缓存一个空值进redis中,这样在缓存穿透的的时候,即使有大量请求进来
                    //也不会因为该值找不到而一直将海量请求打到数据库中,导致数据库宕机
                    stringRedisTemplate.opsForValue().set(userName,localResult);
                    //同时返回异常信息
                    return "数据库中查询到对应数据,将数据写入redis中";
                }else {
                    //如果数据库中也没有查找到,此时就需要我们缓存一个空值进redis中,这样在缓存穿透的的时候,即使有大量请求进来
                    //也不会因为该值找不到而一直将海量请求打到数据库中,导致数据库宕机
                    stringRedisTemplate.opsForValue().set(userName,"null");
                }
            }else {
                Thread.sleep(100);
                return redisTest1(userName);
            }
        }catch (Exception e){
            throw new Exception(e.getMessage());
        }
        return "";
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值