在SpringBoot中利用Redis实现互斥锁

在SpringBoot中利用Redis实现互斥锁

基本知识

前提条件,有一个能够在Springboot中使用Redis的项目,或者能够直接开也行
为什么要实现互斥锁:当我们利用Redis存储热点数据时,突然就过期失效或者被删除了,导致大量请求同时访问数据库,增加了数据库的负载。为减轻数据库的负载我们利用互斥锁。

业务的一个逻辑图流程:

核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止(这个尝试,要重新从Redis再次尝试获取数据,可能别的锁已经获取到了),才能进行查询

如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿

操作锁的核心思路就是利用redis的setnx方法来表示获取锁,该方法含义是redis中如果没有这个key,则插入成功,返回1

具体实现

  • 设置锁,删除锁
java 复制代码
   /**
     * 根据name对特定的数据进行锁
     * @param name
     * @return
     */
public boolean setLock(String name) {
    return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(name, true, 10, TimeUnit.SECONDS));
}

public boolean releaseLock(String name) {
    return Boolean.TRUE.equals(redisTemplate.delete(name));
}
  • 具体流程实现
java 复制代码
@GetMapping("/getOneByLock/{sequence}")
public BaseResponse<Sentences> getOneByLock(@PathVariable long sequence) {
    // 从redis中查信息
    String name = "test:redis:sentences:"+ sequence;
    Sentences sentence = (Sentences) redisTemplate.opsForValue().get(name);
    // 命中返回数据
    if(sentence != null ){
        redisTemplate.expire(name,2,TimeUnit.MINUTES);
        return ResultUtils.success(sentence);
    }
    // 未命中获取锁
    String LOCK_NAME = "test:redis:lock:" + sequence;
    boolean lock = redisTemplate.opsForValue().get(LOCK_NAME) != null && (boolean) redisTemplate.opsForValue().get(LOCK_NAME);
    //如果lock等于false 那么就可以获取到锁并且,锁住不许其他人操作
    if(!lock){
       return ResultUtils.success(setLockReleaseLockAboutSentence(LOCK_NAME,name,sequence));
    }
    // 没有获取到锁 休眠一段时间,并且反复检测redis中的数据是否存在,或者锁是否释放
    while(true){
        try {
            Thread.sleep(1000);
            log.error("等待中");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 检查是否存在值
        sentence =  (Sentences) redisTemplate.opsForValue().get(name);
        if(sentence != null){
            return ResultUtils.success(sentence);
        }
        boolean checkAgain = (boolean) redisTemplate.opsForValue().get(LOCK_NAME);
        if(!checkAgain){
            sentence =  setLockReleaseLockAboutSentence(LOCK_NAME,name,sequence);
        }
        return ResultUtils.success(sentence);
    }
}

public Sentences setLockReleaseLockAboutSentence(String LOCK_NAME,String redisName, long sequence){
    // 设置 锁值 为true
    setLock(LOCK_NAME);
    // 并且从数据中查取数据
    Sentences sentence = sentencesService.getById(sequence);
    // 这里为了明显不能抢锁设置一个睡眠时间
    try {
        log.error("休眠中");
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
//            把数据写入Redis
    redisTemplate.opsForValue().set(redisName,sentence,2, TimeUnit.MINUTES);
    // 释放锁
    releaseLock(LOCK_NAME);
    // 返回数据
    return sentence;
}

代码说明,在这个代码中为了演示明显,获取锁中延迟3s,竞争锁会延迟1s,下面的演示,初始时Redis中没有数据,只能去数据库中取数据,但是设置了互斥锁,所以只能够一个线程进入数据库取数据,其他只能等待数据得到结果。

结果示意

  • redis中无数据
  • 结果

最终效果是好的。redis中已存入数据

相关推荐
程序猿_极客13 分钟前
【免费】分享一套优质的基于SSM的校园失物招领系统的设计与实现,源码+文档+视频详解(讲解)
java·mysql·ssm·课程设计·失物招领系统
白远山1 小时前
上海24小时自助健身房解决方案实战指南与经验分享
java·数据库·架构·需求分析
码兄科技1 小时前
24小时自助健身房系统软件开发实战指南:功能设计与部署方案
java·spring boot·uni-app
程序猿_极客1 小时前
【免费】分享一套优质的基于SpringBoot的扶贫助农管理系统(详解)
java·springboot·课程设计·计算机项目
2603_965148111 小时前
AI+API选品:下一代智能商务助手雏形已现
java·大数据·数据库·人工智能·数据挖掘
wuminyu1 小时前
HotSpot的轻量锁与膨胀后的ObjectMonitor状态重建原理
java·linux·c语言·jvm·c++
lbb 小魔仙1 小时前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
CoderYanger1 小时前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
北冥有鱼被烹1 小时前
perftest 之 ib_write_bw --use-null-mr 参数详解:用 NULL MR 做 RDMA 带宽测试
java