专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Redlock原理简介和实现过程

前言

上篇文章介绍了通过
 SET key_name my_random_value NX PX 30000

NX 表示if not exist 就设置并返回True,否则不设置并返回False

PX 表示过期时间用毫秒级, 30000 表示这些毫秒时间后此key过期 
 方式实现的redis分布锁 
 但有缺点: 
 只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况: 
 在Redis的master节点上拿到了锁; 
 但是这个加锁的key还没有同步到slave节点; 
 master故障,发生故障转移,slave节点升级为master节点; 
 导致锁丢失。 
 由此 redis官方推荐 redlock 来解决这个问题

使用场景

多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击)

优点

  • 防止了 单节点故障造成整个服务停止运行的情况;
  • 在多节点中锁的设计,及多节点同时崩溃等各种意外情况有自己独特的设计方法

概念说明

  • 1.TTL:Time To Live;只 redis key 的过期时间或有效生存时间
  • 2.clock drift:时钟漂移;指两个电脑间时间流速基本相同的情况下,两个电脑(或两个进程间)时间的差值;如果电脑距离过远会造成时钟漂移值 过大

最低保证分布式锁的有效性及安全性的要求

  • 1.互斥;任何时刻只能有一个client获取锁
  • 2.释放死锁;即使锁定资源的服务崩溃或者分区,仍然能释放锁
  • 3.容错性;只要多数redis节点(一半以上)在使用,client就可以获取和释放锁

使用redession实现分布锁的过程

假设有5个完全独立的redis主服务器

  • 1.获取当前时间戳
  • 2.client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。

    比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁

  • 3.client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功
  • 4.如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);
  • 5.如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁

代码实现

maven依赖

<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.4.3</version>
</dependency>

获取redession客户端连接

70_1.png

业务处理接口

70_2.png

具体的业务逻辑实现都需要实现该接口

封装锁管理接口

70_3.png

锁管理接口实现类

70_4.png

调用加锁

70_5.png

redessoin获取锁和释放锁源码分析

获取锁

跟踪RLock类的lock.tryLock(100, lockTime, TimeUnit.SECONDS);方法
 可以找到方法

70_6.png

现在来分析下其中的lua脚本
 if (redis.call('exists', KEYS[1]) == 0) 
 首先分布式锁的KEY不能存在,, 
 then redis.call('hset', KEYS[1], ARGV[2], 1); 
 如果确实不存在,那么执行hset命令(hset REDLOCK_KEY uuid+threadId 1) 
 redis.call('pexpire', KEYS[1], ARGV[1]); 
 并通过pexpire设置失效时间(也是锁的租约时间) 
 if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) 
 如果分布式锁的KEY已经存在,并且value也匹配,表示是当前线程持有的锁, 
 then redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]); 
 那么重入次数加1,并且设置失效时间 
 return redis.call('pttl', KEYS[1]) 
 获取分布式锁的KEY的失效时间毫秒数

释放锁

70_7.png

if (redis.call('exists', KEYS[1]) == 0)
then redis.call('publish', KEYS[2], ARGV[1])
 如果分布式锁KEY不存在,那么向channel发布一条消息 
 if (redis.call('hexists', KEYS[1], ARGV[3]) == 0)
then return nil 
 如果分布式锁存在,但是value不匹配,表示锁已经被占用,那么直接返回 
 local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); 
 如果就是当前线程占有分布式锁,那么将重入次数减1 
 if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); 
 重入次数减1后的值如果大于0,表示分布式锁有重入过,那么只设置失效时间,还不能删除 
 return 0; else redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]); 
 重入次数减1后的值如果为0,表示分布式锁只获取过1次,那么删除这个KEY,并发布解锁消息

DEMO源码

https://gitee.com/pingfanrenbiji/redis-demo

参考文章

https://tech.souyunku.com/rgcLOVEyaya/p/RGC_LOVE_YAYA_1003days.html
https://yq.aliyun.com/articles/674394

本文使用 tech.souyunku.com 排版

文章永久链接:https://tech.souyunku.com/31970

未经允许不得转载:搜云库技术团队 » Redlock原理简介和实现过程

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们