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

缓存击穿概念

缓存击穿也可以理解成热点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
    评论
Redis穿是指一个缓存的key在缓存失效的瞬间,同时有大量的并发请求访问,导致所有的请求都直接访问数据库,造成了数据库短时间内承受大量的请求压力,严重影响网站的性能。 为了解决这个问题,我们可以采用互斥锁的方式来保证只有一个线程可以访问数据库,其他线程都需要等待锁被释放后才能再次尝试获取锁。 下面是使用互斥锁解决Redis穿问题的示例代码: ```python import redis import threading # 创建 Redis 连接池 redis_pool = redis.ConnectionPool(host='localhost', port=6379, db=0) # 创建 Redis 客户端 redis_client = redis.Redis(connection_pool=redis_pool) # 定义互斥锁 mutex_lock = threading.Lock() def get_data(key): # 先尝试从缓存中获取数据 value = redis_client.get(key) # 如果缓存中存在数据,则直接返回 if value: return value # 否则尝试获取锁 with mutex_lock: # 再次尝试从缓存中获取数据 value = redis_client.get(key) # 如果缓存中存在数据,则直接返回 if value: return value # 如果缓存中不存在数据,则从数据库中获取,并写入缓存 value = db.get(key) redis_client.set(key, value) return value ``` 在上面的代码中,我们首先尝试从缓存中获取数据,如果存在则直接返回。如果缓存中不存在数据,则尝试获取互斥锁,再次尝试从缓存中获取数据。如果获取到锁并且从缓存中还是没有获取到数据,则从数据库中获取数据,并写入缓存。最后,释放锁并返回数据。 需要注意的是,使用互斥锁会影响性能,因此应该尽量避免过多的线程同时访问同一个key。同时,还需要合理设置锁的超时时间,避免死锁的发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值