请选择 进入手机版 | 继续访问电脑版

[Redis] Redis分布式非公平锁的利用

[复制链接]
查看110 | 回复30 | 2021-9-13 23:30:16 | 显示全部楼层 |阅读模式
目次

媒介

看了很多博客,和资料,这里只针对redis做分布式锁做一下深入探究 ,渴望 对你们有帮助。网上提供了很多分布式锁的操作,这里逐一举例然后批评 优缺点及改进方案,渴望 如许 子能让当家更好的明白 redis分布式锁。

redis分布式锁第一版

大家应该都知道Redis做分布式锁无非就是INCR下令 或者是SetNx下令 ,这里我们采用setnx下令 。
操作:setnx key 假如 操作成功则代表拿到锁,假如 没有操作成功则代表没有拿到锁。

缺点:假如 这个人拿到锁后宕机了怎么办,那么这个锁就再也不能开释 了。

改进:给这个锁增长 一个过期时间,如许 假如 有效 期过了,那么这个锁就会主动 开释 了。

redis分布式锁第二版

通过上面所说我们应该对redis分布式举行 改进。
操作: 利用 setnx 下令 ,之后,在EXPIREAT key 30000 这条下令 设置key的有效 期为30秒。
这里我们大概 会发现,假如 要是刚setnx竣事 之后,要是宕机了。怎么办?那么我们为了保证原子性,以是 jedis提供了一个原子操作,set(key,value,nx,30,时间单位)如许 便办理 了。
缺点:假如 这个锁的时间不够用怎么办,那么就会导致这个功能锁不住。假设:A拿到锁了,但是A还没有实验 竣事 ,B又拿到锁了,那么A实验 竣事 的时间 是不是会把B的这个锁给删除掉。如许 就导致了锁不住的效果 。
改进:我们可以学习乐观所,给锁的value值是一个唯一的编号,或者版本号,我们每次对锁举行 操作的时间 ,就会去验证这个版本号,还是不是本身 的版本号。假如 不是了就不答应 操作了。

redis分布式锁第三版

通过上面的总结这第三版想必也很简单了。知识多了一个唯一值而已。但是加了唯一值还是改变不了锁不住的效果 ,只是办理 了帮其他的线程解锁的题目 ,那么要怎么样才能锁得住呢?当时我想到的是给他 时间久一点,后来发现着实 再久,也一样会出现锁不住的时间 ,而且太久了假如 宕机了,就会有很长时间机器无法工作,很轻易 造成线程堆积。

redis分布式锁终极 版

由上面我们发现一样寻常 简单实用redis做锁着实 是有很多毛病 和bug的,但是有没有可以或许 办理 这些的呢?当然是有的。
模拟 AQS锁, lock方法实验 完之后,实验 下面代码是被锁的,unlock实验 完,开释 锁。其他线程等待,而不是直接返回错误效果 。

终极 版还是打算先上代码再说,为了方便我把全部 的实现都写在了一个类内里 。

  1. @Autowired
  2. private RedisTemplate redisTemplate;
  3. @Autowired
  4. private RedisUtils redisUtils;
  5. @Autowired(required = false)
  6. private ThreadPoolTaskScheduler threadPoolTaskScheduler;
  7. public final String LOCK_PREFIX = "REDIS_LOCK";
  8. private final Long LOCK_EXPIRE = 30 * 1000L;
  9. private final Long OVER_TIME = 10L;
  10. private Map<String,ScheduledFuture<?> > futureMap = new ConcurrentHashMap<>();
  11. private Jedis jedis;
  12. public Lock() {
  13. }
  14. private ReentrantLock reentrantLock;
  15. /**
  16. * 给线程枷锁
  17. *
  18. * @param key
  19. */
  20. public void lock(String key) {
  21. //自旋获取锁
  22. while (true) {
  23. if (setLock(key)) {//拿锁成功
  24. //获取锁后开启任务
  25. threadPoolTaskScheduler.schedule(()->{
  26. Set<String> keys = scan(LOCK_PREFIX);
  27. Iterator<String> iterator = keys.iterator();
  28. //遍历所有的key 延长key的时间
  29. while (iterator.hasNext()) {
  30. log.info("执行动态定时任务: " + LocalDateTime.now().toLocalTime());
  31. redisUtils.expire(key, Long.valueOf(OVER_TIME), TimeUnit.SECONDS);//延长时间(秒)
  32. }
  33. },new Trigger(){
  34. @Override
  35. public Date nextExecutionTime(TriggerContext triggerContext){
  36. return new CronTrigger("0/10 * * * * ?").nextExecutionTime(triggerContext);
  37. }
  38. });
  39. return;
  40. }
  41. }
  42. }
  43. /**
  44. * setnx
  45. *
  46. * @param key
  47. * @return
  48. */
  49. public boolean setLock(String key) {
  50. String lock = LOCK_PREFIX + key;
  51. return (Boolean) redisTemplate.execute(new RedisCallback<Object>() {
  52. @Override
  53. public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
  54. long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;
  55. Boolean acquire = redisConnection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
  56. if (acquire) {
  57. return true;
  58. } else {
  59. byte[] value = redisConnection.get(lock.getBytes());
  60. if (Objects.nonNull(value) && value.length > 0) {
  61. long expireTime = Long.parseLong(new String(value));
  62. if (expireTime < System.currentTimeMillis()) {
  63. // 如果锁已经过期
  64. byte[] oldValue = redisConnection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
  65. // 防止死锁
  66. return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
  67. }
  68. }
  69. }
  70. return false;
  71. }
  72. });
  73. }
  74. /**
  75. * 删除锁
  76. *
  77. * @param key
  78. */
  79. public void unlock(String key) {
  80. String lock = LOCK_PREFIX + key;
  81. synchronized (this) {
  82. futureMap.get(lock).cancel(true);//停止任务
  83. redisTemplate.delete(lock);
  84. }
  85. }
  86. /**
  87. * 判断key是否存在
  88. *
  89. * @param key 键
  90. * @return true 存在 false不存在
  91. */
  92. public boolean hasKey(String key) {
  93. try {
  94. return redisTemplate.hasKey(key);
  95. } catch (Exception e) {
  96. e.printStackTrace();
  97. return false;
  98. }
  99. }
  100. public Set<String> scan(String key) {
  101. return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
  102. Set<String> keys = Sets.newHashSet();
  103. JedisCommands commands = (JedisCommands) connection.getNativeConnection();
  104. MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands;
  105. ScanParams scanParams = new ScanParams();
  106. scanParams.match("*" + key + "*");
  107. scanParams.count(1000);
  108. ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
  109. while (null != scan.getStringCursor()) {
  110. keys.addAll(scan.getResult());
  111. if (!StringUtils.equals("0", scan.getStringCursor())) {
  112. scan = multiKeyCommands.scan(scan.getStringCursor(), scanParams);
  113. continue;
  114. } else {
  115. break;
  116. }
  117. }
  118. return keys;
  119. });
  120. }
复制代码

分析:

  • 判断 是否获取到锁,获取到锁,继续实验 ,没有获取到锁,自旋继续获取。
  • 获取到锁后调度一个任务 。每10秒实验 一次,并且假如 发现所没有开释 延伸 10秒。
  • 开释 锁,删除掉redis中的key,并竣事 掉对应的锁的任务 。

加锁运行原理:

在这里插入图片形貌

解锁操作原理:

在这里插入图片形貌

解锁操作就比较简单了。但是得为了不出必要的贫苦 ,最好是给制止 锁延时任务 ,和删除所 这两部添加进程 锁,可以利用 synchronized,也可以利用 AQS lock锁。

这里Redis非公平锁详解算是竣事 了,后期大概 会更新利用 Redis,实现公平锁,谢谢大家的支持,假如 有必要 的小伙伴可以直接拿走,渴望 能给大家带来帮助。

在这里我渴望 看过文章的小伙伴可以或许 根绝实现原理本身 去实现,如许 可以帮助小伙伴明白 非公平锁机制,和Redis实现非公平,假如 不喜欢 本身 去实现的话,这里我给大家保举 一个Redission 这个插件,这个插件是一个Redis锁的很好的一个实现,大家可以直接用这个。具体 怎么用就不讲解了,操作非常简单。

到此这篇关于Redis分布式非公平锁的利用 的文章就先容 到这了,更多干系 Redis分布式非公平锁内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章渴望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 心随674 | 2021-9-19 20:09:00 | 显示全部楼层
看帖回帖一条路!
回复

使用道具 举报

avatar chuzhaofeng | 2021-9-26 18:45:59 | 显示全部楼层
我只是来赚积分的!
回复

使用道具 举报

avatar 維健_廣律 | 2021-10-1 06:15:22 | 显示全部楼层
admin楼主又闹绯闻了!
回复

使用道具 举报

avatar 工兵班长纬 | 2021-10-3 09:14:56 | 显示全部楼层
admin楼主加油,看好你哦!
回复

使用道具 举报

avatar auqsio7413820 | 2021-10-4 10:00:18 | 显示全部楼层
admin楼主的头像能辟邪啊!
回复

使用道具 举报

avatar V刘晨曦 | 2021-10-6 13:55:39 | 显示全部楼层
兰州烧饼,鉴定完毕!
回复

使用道具 举报

avatar 一箫凝月黄 | 2021-10-8 04:07:59 | 显示全部楼层
最近回了很多帖子,都没人理我!
回复

使用道具 举报

avatar 123457166 | 2021-10-8 11:29:17 | 显示全部楼层
admin楼主是一个神奇的青年!
回复

使用道具 举报

avatar 123457176 | 2021-10-9 09:24:01 | 显示全部楼层
楼上的能详细介绍一下么?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则