什么是分布式锁?几种分布式锁分别是怎么实现的?

开发者 2024-9-25 20:38:58 98 0 来自 中国
保举学习

分布式-百口桶(口试+技能):分布式锁+分布式变乱+分布式缓存,redis+zk+nginx+mq+kafka等,必须死磕!

一、什么是分布式锁:

1、什么是分布式锁:
分布式锁,即分布式体系中的锁。在单体应用中我们通过锁办理的是控制共享资源访问的标题,而分布式锁,就是办理了分布式体系中控制共享资源访问的标题。与单体应用差别的是,分布式体系中竞争共享资源的最小<typo id="typo-135" data-origin="粒度" ignoretag="true">粒度</typo>从线程升级成了历程。
2、分布式锁应该具备哪些条件:

  • 在分布式体系情况下,一个方法在同一时间只能被一个呆板的一个线程实行
  • 高可用的获取锁与开释锁
  • 高性能的获取锁与开释锁
  • 具备可重入特性(可理解为重新进入,由多于一个使命并发使用,而不必担心数据错误)
  • 具备锁失效机制,即主动解锁,防止死锁
  • 具备非壅闭锁特性,即没有获取到锁将直接返回获取锁失败
3、分布式锁的实现方式:
基于数据库实现分布式锁基于Zookeeper实现分布式锁基于reids实现分布式锁
这篇文章就简朴先容下这几种分布式锁的实现,重点解说的是基于redis的分布式锁。
二、基于数据库的分布式锁:

基于数据库的锁实现也有两种方式,一是基于数据库表的增删,另一种是基于数据库排他锁。
1、基于数据库表的增删:
基于数据库表增删是最简朴的方式,起首创建一张锁的表重要包罗下列字段:类的全路径名+方法名,时间戳等字段。
具体的使用方式:当须要锁住某个方法时,往该表中插入一条相干的记载。类的全路径名+方法名是有唯一性束缚的,如果有多个哀求同时提交到数据库的话,数据库会包管只有一个操纵可以乐成,那么我们就以为操纵乐成的谁人线程得到了该方法的锁,可以实行方法体内容。实行完毕之后,须要delete该记载。
(这里只是简朴先容一下,对于上述方案可以举行优化,如:应用主从数据库,数据之间双向同步;一旦挂掉快速切换到备库上;做一个定时使命,每隔肯定时间把数据库中的超时数据整理一遍;使用while循环,直到insert乐成再返回乐成;记载当前得到锁的呆板的主机信息和线程信息,下次再获取锁的时间先查询数据库,如果当前呆板的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了,实现可重入锁)
2、基于数据库排他锁:
基于MySql的InnoDB引擎,可以使用以下方法来实现加锁操纵:
public void lock(){    connection.setAutoCommit(false)    int count = 0;    while(count < 4){        try{            select * from lock where lock_name=xxx for update;            if(效果不为空){                //代表获取到锁                return;            }        }catch(Exception e){        }        //为空大概抛异常的话都表示没有获取到锁        sleep(1000);        count++;    }    throw new LockException();}在查询语句背面增长for update,数据库会在查询过程中给数据库表增长排他锁。得到排它锁的线程即可得到分布式锁,当得到锁之后,可以实行方法的业务逻辑,实行完方法之后,开释锁connection.commit()。当某条记载被加上排他锁之后,其他线程无法获取排他锁并被壅闭。
3、基于数据库锁的优缺点:
上面两种方式都是依靠数据库表,一种是通过表中的记载判断当前是否有锁存在,别的一种是通过数据库的排他锁来实现分布式锁。

  • 长处是直接借助数据库,简朴轻易理解。
  • 缺点是操纵数据库须要肯定的开销,性能标题须要思量。
三、基于Zookeeper的分布式锁

基于zookeeper临时有序节点可以实现的分布式锁。每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目次下,天生一个唯一的瞬时有序节点。 判断是否获取锁的方式很简朴,只须要判断有序节点中序号最小的一个。 当开释锁的时间,只需将这个瞬时节点删除即可。同时,其可以制止服务宕机导致的锁无法开释,而产生的死锁标题。 (第三方库有 Curator,Curator提供的InterProcessMutex是分布式锁的实现)
Zookeeper实现的分布式锁存在两个个缺点:

  • (1)性能上大概并没有缓存服务那么高,由于每次在创建锁和开释锁的过程中,都要动态创建、烧毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来实行,然后将数据同步到全部的Follower呆板上。
  • (2)zookeeper的并发安全标题:由于大概存在网络抖动,客户端和ZK集群的session毗连断了,zk集群以为客户端挂了,就会删除临时节点,这时间其他客户端就可以获取到分布式锁了。
四、基于redis的分布式锁:

redis下令阐明:
(1)setnx下令:set if not exists,当且仅当 key 不存在时,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。

  • 返回1,阐明该历程得到锁,将 key 的值设为 value
  • 返回0,阐明其他历程已经得到了锁,历程不能进入临界区。
下令格式:setnx lock.key lock.value
(2)get下令:获取key的值,如果存在,则返回;如果不存在,则返回nil
下令格式:get lock.key
(3)getset下令:该方法是原子的,对key设置newValue这个值,而且返回key原来的旧值。
下令格式:getset lock.key newValue
(4)del下令:删除redis中指定的key
下令格式:del lock.key
方案一:基于set下令的分布式锁

1、加锁:使用setnx举行加锁,当该指令返回1时,阐明乐成得到锁
2、解锁:当得到锁的线程实行完使命之后,使用del下令开释锁,以便其他线程可以继承实行setnx下令来得到锁
(1)存在的标题:假设线程获取了锁之后,在实行使命的过程中挂掉,来不及体现地实行del下令开释锁,那么竞争该锁的线程都会实行不了,产存亡锁的情况。
(2)办理方案:设置锁超时时间
3、设置锁<typo id="typo-2688" data-origin="超时" ignoretag="true">超时</typo>时间:setnx 的 key 必须设置一个超时时间,以包管即使没有被显式开释,这把锁也要在肯定时间后主动开释。可以使用expire下令设置锁超时时间
(1)存在标题:
setnx 和 expire 不是原子性的操纵,假设某个线程实行setnx 下令,乐成得到了锁,但是还没来得及实行expire 下令,服务器就挂掉了,如许一来,这把锁就没有设置逾期时间了,酿成了死锁,别的线程再也没有办法得到锁了。
(2)办理方案:redis的set下令支持在获取锁的同时设置key的逾期时间
4、使用set下令加锁并设置锁逾期时间:
下令格式:set <lock.key> <lock.value> nx ex <expireTime>
详情参考redis使用文档:http://doc.redisfans.com/string/set.html
(1)存在标题:
① 如果线程A乐成得到了锁,而且设置的超时时间是 30 秒。如果某些缘故原由导致线程 A 实行<typo id="typo-3121" data-origin="的" ignoretag="true">的</typo>很慢,过了 30 秒都没实行完,这时间锁逾期主动开释,线程 B 得到了锁。
② 随后,线程A实行完使命,接着实行del指令来开释锁。但这时间线程 B 还没实行完,线程A现实上删除的是线程B加的锁。
(2)办理方案:
可以在 del 开释锁之前做一个判断,验证当前的锁是不是本身加的锁。在加锁的时间把当前的线程 ID 当做value,并在删除之前验证 key 对应的 value 是不是本身线程的 ID。但是,如许做实在隐含了一个新的标题,get操纵、判断和开释锁是两个独立操纵,不是原子性。对于非原子性的标题,我们可以使用Lua脚原来确保操纵的原子性
5、锁续期:(这种机制类似于redisson的看门狗机制,文章背面会具体阐明)
虽然步调4制止了线程A误删掉key的情况,但是同一时间有 A,B 两个线程在访问代码块,仍旧是不完善的。怎么办呢?我们可以让得到锁的线程开启一个保卫线程,用来给快要逾期的锁“续期”。
① 假设线程A实行了29 秒后还没实行完,这时间保卫线程会实行 expire 指令,为这把锁续期 20 秒。保卫线程从第 29 秒开始实行,每 20 秒实行一次。
② 情况一:当线程A实行完使命,会显式关掉保卫线程。
③ 情况二:如果服务器忽然断电,由于线程 A 和保卫线程在同一个历程,保卫线程也会停下。这把锁到了超时的时间,没人给它续命,也就主动开释了。
方案二:基于setnx、get、getset的分布式锁

1、实现原理:
(1)setnx(lockkey, 当前时间+逾期超时时间) ,如果返回1,则获取锁乐成;如果返回0则没有获取到锁,转向步调(2)
(2)get(lockkey)获取值oldExpireTime ,并将这个value值与当前的体系时间举行比力,如果小于当前体系时间,则以为这个锁已经超时,可以答应别的哀求重新获取,转向步调(3)
(3)盘算新的逾期时间 newExpireTime=当前时间+锁超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime
(4)判断 currentExpireTime 与 oldExpireTime 是否相称,如果相称,阐明当前getset设置乐成,获取到了锁。如果不相称,阐明这个锁又被别的哀求获取走了,那么当前哀求可以直接返回失败,大概继承重试。
(5)在获取到锁之后,当火线程可以开始本身的业务处置惩罚,当处置惩罚完毕后,比力本身的处置惩罚时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接实行del下令开释锁(开释锁之前须要判断持有锁的线程是不是当火线程);如果大于锁设置的超时时间,则不须要再锁举行处置惩罚。
2、代码实现:
(1)获取锁的实现方式:
public boolean lock(long acquireTimeout, TimeUnit timeUnit) throws InterruptedException {    acquireTimeout = timeUnit.toMillis(acquireTimeout);    long acquireTime = acquireTimeout + System.currentTimeMillis();    //使用J.U.C的ReentrantLock    threadLock.tryLock(acquireTimeout, timeUnit);    try {        //循环实行        while (true) {            //调用tryLock            boolean hasLock = tryLock();            if (hasLock) {                //获取锁乐成                return true;            } else if (acquireTime < System.currentTimeMillis()) {                break;            }            Thread.sleep(sleepTime);        }    } finally {        if (threadLock.isHeldByCurrentThread()) {            threadLock.unlock();        }    }    return false;}public boolean tryLock() {    long currentTime = System.currentTimeMillis();    String expires = String.valueOf(timeout + currentTime);    //设置互斥量    if (redisHelper.setNx(mutex, expires) > 0) {        //获取锁,设置超时时间        setLockStatus(expires);        return true;    } else {        String currentLockTime = redisUtil.get(mutex);        //查抄锁是否超时        if (Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) < currentTime) {            //获取旧的锁时间并设置互斥量            String oldLockTime = redisHelper.getSet(mutex, expires);            //旧值与当前时间比力            if (Objects.nonNull(oldLockTime) && Objects.equals(oldLockTime, currentLockTime)) {                //获取锁,设置超时时间                setLockStatus(expires);                return true;            }        }        return false;    }}tryLock方法中,重要逻辑如下:lock调用tryLock方法,参数为获取的超时时间与单位,线程在超时时间内,获取锁操纵将自旋在那边,直到该自旋锁的保持者开释了锁。
(2)开释锁的实现方式:
public boolean unlock() {    //只有锁的持有线程才气解锁    if (lockHolder == Thread.currentThread()) {        //判断锁是否超时,没有超时才将互斥量删除        if (lockExpiresTime > System.currentTimeMillis()) {            redisHelper.del(mutex);            logger.info("删除互斥量[{}]", mutex);        }        lockHolder = null;        logger.info("开释[{}]锁乐成", mutex);        return true;    } else {        throw new IllegalMonitorStateException("没有获取到锁的线程无法实行解锁操纵");    }}存在标题:
(1)这个锁的核心是基于System.currentTimeMillis(),如果多台服务器时间差别等,那么标题就出现了,但是这个bug完全可以从服务器运维层面规避的,而且如果服务器时间不一样的话,只要和时间相干的逻辑都是会出标题的
(2)如果前一个锁超时的时间,刚好有多台服务器去哀求获取锁,那么就会出现同时实行redis.getset()而导致出现逾期时间覆盖标题,不外这种情况并不会对精确效果造成影响
(3)存在多个线程同时持有锁的情况:如果线程A实行使命的时间凌驾锁的逾期时间,这时另一个线程就可以得到这个锁了,造成多个线程同时持有锁的情况。类似于方案一,可以使用“锁续期”的方式来办理。
前两种redis分布式锁的存在的标题
前面两种redis分布式锁的实现方式,如果从“高可用”的层面来看,仍旧是有所短缺,也就是说当 redis 是单点的情况下,当发生故障时,则整个业务的分布式锁都将无法使用。
为了进步可用性,我们可以使用主从模式大概哨兵模式,但在这种情况下仍旧存在标题,在主从模式大概哨兵模式下,正常情况下,如果加锁乐成了,那么master节点会异步复制给对应的slave节点。但是如果在这个过程中发生master节点宕机,主备切换,slave节点从变为了 master节点,而锁还没从旧master节点同步过来,这就发生了锁丢失,会导致多个客户端可以同时持有同一把锁的标题。来看个图来想下这个过程:
那么,怎样制止这种情况呢?redis 官方给出了基于多个 redis 集群摆设的高可用分布式锁办理方案:RedLock,在方案三我们就来具体先容一下。(备注:如果master节点宕机期间,可以容忍多个客户端同时持有锁,那么就不须要redLock)
方案三:基于RedLock的分布式锁

redLock的官方文档地点:https://redis.io/topics/distlock
Redlock算法是Redis的作者 Antirez 在单Redis节点基础上引入的高可用模式。Redlock的加锁要联合单节点分布式锁算法共同实现,由于它是RedLock的基础
1、加锁实现原理:
如今假设有5个Redis主节点(大于3的奇数个),如许根本包管他们不会同时都宕掉,获取锁和开释锁的过程中,客户端会实行以下操纵:
(1)获取当前Unix时间,以毫秒为单位,并设置超时时间TTL
TTL 要大于 正常业务实行的时间 + 获取全部redis服务斲丧时间 + 时钟漂移
(2)依次实行从5个实例,使用雷同的key和具有唯一性的value获取锁,当向Redis哀求获取锁时,客户端应该设置一个网络毗连和相应超时时间,这个超时时间应该小于锁的失效时间TTL,如许可以制止客户端死等。好比:TTL为5s,设置获取锁最多用1s,以是如果一秒内无法获取锁,就放弃获取这个锁,从而实行获取下个锁
(3)客户端 获取全部能获取的锁后的时间 减去 第(1)步的时间,就得到锁的获取时间。锁的获取时间要小于锁失效时间TTL,而且至少从半数以上的Redis节点取到锁,才算获取乐成锁
(4)如果乐成得到锁,key的真正有效时间 = TTL - 锁的获取时间 - 时钟漂移。好比:TTL 是5s,获取全部锁用了2s,则真正锁有效时间为3s
(5)如果由于某些缘故原由,获取锁失败(没有在半数以上实例取到锁大概取锁时间已经凌驾了有效时间),客户端应该在全部的Redis实例上举行解锁,无论Redis实例是否加锁乐成,由于大概服务端相应消息丢失了但是现实乐成了。
假想如许一种情况:客户端发给某个Redis节点的获取锁的哀求乐成到达了该Redis节点,这个节点也乐成实行了SET操纵,但是它返回给客户端的相应包却丢失了。这在客户端看来,获取锁的哀求由于超时而失败了,但在Redis这边看来,加锁已经乐成了。因此,开释锁的时间,客户端也应该对当时获取锁失败的那些Redis节点同样发起哀求。现实上,这种情况在异步通讯模子中是有大概发生的:客户端向服务器通讯是正常的,但反方向却是有标题的。
(6)失败重试:当client不能获取锁时,应该在随机时间后重试获取锁;同时重试获取锁要有肯定次数限定;
在随机时间后举行重试,重要是防止过多的客户端同时实行去获取锁,导致相互都获取锁失败的标题。
算法表示图如下:
2、RedLock性能及瓦解恢复的相干办理方法:
由于N个Redis节点中的大多数能正常工作就能包管Redlock正常工作,因此理论上它的可用性更高。前面我们说的主从架构下存在的安全性标题,在RedLock中已经不存在了,但如果有节点发生瓦解重启,还是会对锁的安全性有影响的,具体的影响程度跟Redis持久化配置有关:
(1)如果redis没有持久化功能,在clientA获取锁乐成后,全部redis重启,clientB可以大概再次获取到锁,如许违法了锁的排他互斥性;
(2)如果启动AOF永世化存储,事变会好些, 举例:当我们重启redis后,由于redis逾期机制是按照unix时间戳走的,以是在重启后,然后会按照规定的时间逾期,不影响业务;但是由于AOF同步到磁盘的方式默认是每秒一次,如果在一秒内断电,会导致数据丢失,立即重启会造成锁互斥性失效;但如果同步磁盘方式使用Always(每一个写下令都同步到硬盘)造成性能急剧降落;以是在锁完全有效性和性能方面要有所弃取;
(3)为了有效办理既包管锁完全有效性 和 性能高效标题:antirez又提出了“延长重启”的概念,redis同步到磁盘方式保持默认的每秒1次,在redis瓦解单机后(无论是一个还是全部),先不立即重启它,而是等待TTL时间后再重启,如许的话,这个节点在重启前所到场的锁都会逾期,它在重启后就不会对现有的锁造成影响,缺点是在TTL时间内服务相称于停息状态;
3、Redisson中RedLock的实现:
在JAVA的redisson包已经实现了对RedLock的封装,重要是通过 redisClient 与 lua 脚本实现的,之以是使用 lua 脚本,是为了实现加解锁校验与实行的变乱性。
(1)唯一ID的天生:
分布式变乱锁中,为了可以大概让作为中心节点的存储节点获取锁的持有者,从而制止锁被非持有者误解锁,每个发起哀求的 client 节点都必须具有全局唯一的 id。通常我们是使用 UUID 来作为这个唯一 id,redisson 也是如许实现的,在此基础上,redisson 还参加了 threadid 制止了多个线程反复获取 UUID 的性能斲丧
protected final UUID id = UUID.randomUUID();String getLockName(long threadId) {    return id + ":" + threadId;}(2)加锁逻辑:
redisson 加锁的核心代码非常轻易理解,通过传入 TTL 与唯一 id,实现一段时间的加锁哀求。下面是可重入锁的实现逻辑:
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {    internalLockLeaseTime = unit.toMillis(leaseTime);    // 获取锁时向5个redis实例发送的下令    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,              // 校验分布式锁的KEY是否已存在,如果不存在,那么实行hset下令(hset REDLOCK_KEY uuid+threadId 1),并通过pexpire设置失效时间(也是锁的租约时间)              "if (redis.call('exists', KEYS[1]) == 0) then " +                  "redis.call('hset', KEYS[1], ARGV[2], 1); " +                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +                  "return nil; " +              "end; " +              // 如果分布式锁的KEY已存在,则校验唯一 id,如果唯一 id 匹配,表示是当火线程持有的锁,那么重入次数加1,而且设置失效时间              "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +                  "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +                  "return nil; " +              "end; " +              // 获取分布式锁的KEY的失效时间毫秒数              "return redis.call('pttl', KEYS[1]);",              // KEYS[1] 对应分布式锁的 key;ARGV[1] 对应 TTL;ARGV[2] 对应唯一 id                Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}(3)开释锁逻辑:
protected RFuture<Boolean> unlockInnerAsync(long threadId) {    // 向5个redis实例都实行如下下令    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,            // 如果分布式锁 KEY 不存在,那么向 channel 发布一条消息            "if (redis.call('exists', KEYS[1]) == 0) then " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; " +            "end;" +            // 如果分布式锁存在,但是唯一 id 不匹配,表示锁已经被占用            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +                "return nil;" +            "end; " +            // 如果就是当火线程占据分布式锁,那么将重入次数减 1            "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +            // 重入次数减1后的值如果大于0,表示分布式锁有重入过,那么只设置失效时间,不删除            "if (counter > 0) then " +                "redis.call('pexpire', KEYS[1], ARGV[2]); " +                "return 0; " +            "else " +                // 重入次数减1后的值如果为0,则删除锁,并发布解锁消息                "redis.call('del', KEYS[1]); " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; "+            "end; " +            "return nil;",            // KEYS[1] 表示锁的 key,KEYS[2] 表示 channel name,ARGV[1] 表示解锁消息,ARGV[2] 表示 TTL,ARGV[3] 表示唯一 id            Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));}(4)redisson中RedLock的使用:
Config config = new Config();config.useSentinelServers()        .addSentinelAddress("127.0.0.1:6369","127.0.0.1:6379", "127.0.0.1:6389")        .setMasterName("masterName")        .setPassword("password").setDatabase(0);RedissonClient redissonClient = Redisson.create(config);RLock redLock = redissonClient.getLock("REDLOCK_KEY");try {    // 实行加锁,最多等待500ms,上锁以后10s主动解锁    boolean isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);    if (isLock) {        //获取锁乐成,实行对应的业务逻辑    }} catch (Exception e) {    e.printStackTrace();} finally {    redLock.unlock();}可以看到,redisson 包的实现中,通过 lua 脚本校验相识锁时的 client 身份,以是我们无需再在 finally 中去判断是否加锁乐成,也无需做额外的身份校验,可以说已经到达开箱即用的程度了。
同样,基于RedLock实现的分布式锁也存在 client 获取锁之后,在 TTL 时间内没有完成业务逻辑的处置惩罚,而此时锁会被主动开释,造成多个线程同时持有锁的标题。而Redisson 在实现的过程中,天然也思量到了这一标题,redisson 提供了一个“看门狗”的特性,当锁即将逾期还没有开释时,不绝的延伸锁key的生存时间。(具体实现原剖析在方案四举行先容)
方案四:基于Redisson看门狗的分布式锁

前面说了,如果某些缘故原由导致持有锁的线程在锁逾期时间内,还没实行完使命,而锁由于还没超时被主动开释了,那么就会导致多个线程同时持有锁的征象出现,而为相识决这个标题,可以举行“锁续期”。实在,在JAVA的Redisson包中有一个"看门狗"机制,已经帮我们实现了这个功能。
1、redisson原理:
redisson在获取锁之后,会维护一个看门狗线程,当锁即将逾期还没有开释时,不绝的延伸锁key的生存时间
4.png 2、加锁机制:
线程去获取锁,获取乐成:实行lua脚本,生存数据到redis数据库。
线程去获取锁,获取失败:不绝通过while循环实行获取锁,获取乐成后,实行lua脚本,生存数据到redis数据库。
3、watch dog主动延期机制:
看门狗启动后,对团体性能也会有肯定影响,默认情况下看门狗线程是不启动的。如果使用redisson举行加锁的同时设置了锁的逾期时间,也会导致看门狗机制失效。
redisson在获取锁之后,会维护一个看门狗线程,在每一个锁设置的逾期时间的1/3处,如果线程还没实行完使命,则不绝延伸锁的有效期。看门狗的查抄锁超时时间默认是30秒,可以通过 lockWactchdogTimeout 参数来改变。
加锁的时间默认是30秒,如果加锁的业务没有实行完,那么每隔 30 ÷ 3 = 10秒,就会举行一次续期,把锁重置成30秒,包管解锁前锁不会主动失效。
那万一业务的呆板宕机了呢?如果宕机了,那看门狗线程就实行不了了,就续不了期,那天然30秒之后锁就解开了呗。
4、redisson分布式锁的关键点:
a. 对key不设置逾期时间,由Redisson在加锁乐成后给维护一个watchdog看门狗,watchdog负责定时监听并处置惩罚,在锁没有被开释且快要逾期的时间主动对锁举行续期,包管解锁前锁不会主动失效
b. 通过Lua脚本实现了加锁息争锁的原子操纵
c. 通过记载获取锁的客户端id,每次加锁时判断是否是当前客户端已经得到锁,实现了可重入锁。
5、Redisson的使用:
在方案三中,我们已经演示了基于Redisson的RedLock的使用案例,实在 Redisson 也封装 可重入锁(Reentrant Lock)、公平锁(Fair Lock)、联锁(MultiLock)、红锁(RedLock)、读写锁(ReadWriteLock)、 信号量(Semaphore)、可逾期性信号量(PermitExpirableSemaphore)、 闭锁(CountDownLatch)等,具体使用阐明可以参考官方文档:Redisson的分布式锁和同步器
附:redLock的官方文档翻译
5.png
作者:张维鹏
原文链接:https://blog.csdn.net/a745233700/article/details/88084219
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:21, Processed in 0.170822 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表