在SpringBoot中如何利用Redis实现互斥锁
作者:IDIOT___IDIOT
当我们利用Redis存储热点数据时,突然就过期失效或者被删除了,导致大量请求同时访问数据库,增加了数据库的负载,为减轻数据库的负载我们利用互斥锁,本文重点介绍在SpringBoot中如何利用Redis实现互斥锁,感兴趣的朋友一起看看吧
在SpringBoot中利用Redis实现互斥锁
基本知识
前提条件,有一个能够在Springboot中使用Redis的项目,或者能够直接开也行
为什么要实现互斥锁:当我们利用Redis存储热点数据时,突然就过期失效或者被删除了,导致大量请求同时访问数据库,增加了数据库的负载。为减轻数据库的负载我们利用互斥锁。
业务的一个逻辑图流程:
核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止(这个尝试,要重新从Redis再次尝试获取数据,可能别的锁已经获取到了),才能进行查询
如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿
操作锁的核心思路就是利用redis的setnx方法来表示获取锁,该方法含义是redis中如果没有这个key,则插入成功,返回1
具体实现
设置锁,删除锁
/** * 根据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)); }
具体流程实现
@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中已存入数据
到此这篇关于在SpringBoot中利用Redis实现互斥锁的文章就介绍到这了,更多相关SpringBoot互斥锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!