您现在的位置:首页 >> 装修知识

聊聊分布式锁的多种做到!

发布时间:2025/08/08 12:18    来源:翔安家居装修网

>if(resourceLock.getLockFlag=='0'&&'S'.equals(resourceLock.getstatus)

&& new Date()>=resourceLock.addDateTime(resourceLock.getBeginTime(,time)) resourceLock.setLockFlag(1); //上吊

resourceLock.setStatus(P); //妥善处理中会

resourceLock.setBeginTime(new Date());

//update resourceLock;

return true;

}else if(new Date()>=resourceLock.addDateTime(resourceLock.getBeginTime(,time)) //延时未但亦会执行者结束,提供吊不甘心

return false;

}else return false;

}

}

解吊unlock法则的所谓编译器如下:

public void unlock(String v,status) resourceLock.setLockFlag(0); //解吊

resourceLock.setStatus(status); S:说明顺利,F说明不甘心

//update resourceLock;

return ;

}

整体流程:

tryif(lock(keyResource,time)){ //加吊

status = process();//你的的业务逻辑妥善处理。

}

} finally unlock(keyResource,status); //被囚吊

}

其实这个悲观吊意味着的分布式吊,整体的流程还是相当简洁的。就是先select ... for update 吊住主共价键key_resource那个就有,如果为空,则可以插进一条就有,如果已有就有适当下状态和小时,到底仍未延时。这里所需请注意一下哈,必须要加日常事务哈。

2.2 数据源乐观吊意味着的分布式吊

除了悲观吊,还可以用乐观吊意味着分布式吊。乐观吊,顾名思义,就是很乐观,每次预览加载,都觉得不亦会长期存在所发冲突,只有预览不甘心后,才重试。它是基于CAS意识形态意味着的。我那时候所的公司,扣除额度就是用这种建议书。

搞个version字段,每次预览修改,都亦会自增加一,然后去预览额度时,把查出来的那个新版本号,丢下条件去预览,如果是上次那个新版本号,就预览,如果不是,说明别人所发修改过了,就一直重试。

大概流程如下:

查询新版本号和额度

select version,balance from account where user_id ='666';

理论上查到新版本号是oldVersion=1.

逻辑妥善处理,适当额度

if(balance<扣除额度) return;

}

left_balance = balance - 扣除额度;

同步进行扣除额度

update account set balance = #{left_balance} ,version = version+1 where version

= #{oldVersion} and balance>= #{left_balance} and user_id ='666';

大家可以看下这个流程图哈:

这种作法适合于所发不很低的桥段,一般所需另设一下重试的连续

3.基于Redis意味着的分布式吊

Redis分布式吊一般有以下这几种意味着作法:

setnx + expire setnx + value差值是到期小时 set的扩张指令(set ex px nx) set ex px nx + UDP唯一随机差值,再移除 Redisson Redisson + RedLock 3.1 setnx + expire

聊到Redis分布式吊,很多小好朋友快攻就是setnx + expire,如下:

if(jedis.setnx(key,lock_value) == 1){ //setnx加吊

expire(key,100); //另设到期小时

try do something //的业务妥善处理

}catch() }

finally jedis.del(key); //被囚吊

}

}

这段编译器是可以加吊顺利,但是你有不了有辨认出难题,加吊加载和另设延时小时是分离的。理论上在执行者完毕setnx加吊后,正要执行者expire另设到期小时时,数据流crash掉或者要后续保障了,那这个吊就长生不老了,别的多线程永远提供足足吊啦,所以分布式吊不用这么意味着!

3.2 setnx + value差值是到期小时

long expires = System.currentTimeMillis() + expireTime; //系统小时+另设的到期小时

String expiresStr = String.valueOf(expires);

// 如果意味着所吊不长期存在,来到加吊顺利

if (jedis.setnx(key, expiresStr) == 1) return true;

}

// 如果吊仍未长期存在,提供吊的到期小时

String currentValueStr = jedis.get(key);

// 如果提供到的到期小时,相等系统意味着所小时,说明仍未到期

if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis())

// 吊已到期,提供上一个吊的到期小时,并另设直到现在吊的到期小时(不探究redis的getSet指令的小好朋友,可以去官网看下哈)

String oldValueStr = jedis.getSet(key, expiresStr);

if (oldValueStr != null && oldValueStr.equals(currentValueStr)) // 重新考虑多多线程所发的情况,只有一个多线程的另设差值和意味着所差值相近,它才可以加吊

return true;

}

}

//其他情况,均来到加吊不甘心

return false;

}

日常开发中会,有些小好朋友就是这么意味着分布式吊的,但是亦会有这些缺点:

到期小时是服务器端自己转化成的,分布式状况下,每个服务器端的小时必须同步。 不了有保存持有者的唯一上标,不太可能被别的服务器端被囚/解吊。 吊到期的时候,所发多个服务器端同时乞求上去,都执行者了jedis.getSet(),事与愿违只能有一个服务器端加吊顺利,但是该服务器端吊的到期小时,不太可能被别的服务器端覆盖。 3.3 set的扩张指令(set ex px nx)

这个指令的几个参数分别说明什么意思呢?跟大家复习一下:

SET key value [EX seconds] [PX milliseconds] [NX|XX]

EX second :另设共价键的到期小时为second秒。 PX millisecond :另设共价键的到期小时为millisecond毫秒。 NX :只在共价键不长期存在时,才对共价键同步进行另设加载。 XX :只在共价键仍未长期存在时,才对共价键同步进行另设加载。

if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ //加吊

try do something //的业务妥善处理

}catch() }

finally jedis.del(key); //被囚吊

}

}

这个建议书不太可能长期存在这样的难题:

吊到期被囚了,的业务还不了执行者完毕。 吊被别的多线程误删。

有些好朋友不太可能亦会有个疑问,就是吊为什么亦会被别的多线程误删呢?理论上所发多多线程桥段下,多线程A赢取了吊,但是它不了被囚吊的话,多线程B是提供足足吊的,所以按道理它是执行者足足加吊前面的编译器加水,怎么亦会导致吊被别的多线程误删呢?

理论上多线程A和B,都想用key加吊,最后A抢到吊加吊顺利,但是由于执行者的业务逻辑的为时很长,最多了另设的延时小时100s。这时候,Redis就启动时被囚了key吊。这时候多线程B就可以加吊顺利了,接下啦,它也执行者的业务逻辑妥善处理。理论上碰巧这时候,A执行者完毕自己的的业务逻辑,它就去被囚吊,但是它就把B的吊给被囚了。

3.4 set ex px nx + UDP唯一随机差值,再移除

为探究决吊被别的多线程误删难题。可以在set ex px nx的基础上,加上个UDP的唯一随机差值,如下:

if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ //加吊

try do something //的业务妥善处理

}catch() }

finally //适当确实意味着所多线程加的吊,是才被囚

if (uni_request_id.equals(jedis.get(key))) jedis.del(key); //被囚吊

}

}

}

在这里,适当意味着所多线程加的吊和被囚吊不是一个原孙子加载。如果指令行jedis.del()被囚吊的时候,不太可能这把吊仍未不属于意味着所服务器端,亦会冻结他人加的吊。

一般可以用lua分镜来包一下。lua分镜如下:

if redis.call('get',KEYS[1]) == ARGV[1] then

return redis.call('del',KEYS[1])

else

return 0

end;

这种作法相当不错了,一般情况下,仍未可以用于这种意味着作法。但是还是长期存在:吊到期被囚了,的业务还不了执行者完毕的难题。

3.5 Redisson

对于不太可能长期存在吊到期被囚,的业务不了执行者完毕的难题。我们可以稍微把吊到期小时另设长一些,少于但亦会的业务妥善处理小时就好啦。如果你觉得不是很稳,还可以给赢取吊的多线程,开启一个时则守护多线程,每隔一段小时检查吊到底还长期存在,长期存在则对吊的到期小时拉长,防范吊到期日前所被囚。

意味着所开源开放性Redisson化解了这个难题。可以看下Redisson最底层原理图:

只要多线程一加吊顺利,就亦会启动一个watch dog特罗斯季亚涅齐,它是一个一人多线程,亦会每隔10秒检查一下,如果多线程1还持有吊,那么就亦会大幅的拉长吊key的生存小时。因此,Redisson就是用于watch dog化解了吊到期被囚,的业务不了执行者完毕难题。

3.6 Redisson + RedLock

前所面六种建议书都只是基于Redis单机版的分布式吊讨论,还不是很近乎。因为Redis一般都是战斗群部署的:

如果多线程一在Redis的master路由注记上拿到了吊,但是加吊的key还不了同步到slave路由注记。恰好这时,master路由注记愈演愈烈系统故障,一个slave路由注记就亦会强化为master路由注记。多线程二就可以顺理成章提供同个key的吊啦,但多线程一也仍未拿到吊了,吊的安全性就不了了。

为探究决这个难题,Redis作者antirez提出一种很低级的分布式吊算法:Redlock。它的终极目标是这样的:

部署多个Redis master,以保证它们不亦会同时松岛掉。并且这些master路由注记是完毕全相互法理的,相互之间不长期存在数据同步。同时,所需确保在这多个master实例上,是与在Redis单实例,用于相近法则来提供和被囚吊。

我们理论上意味着所有5个Redis master路由注记,在5台服务器上面行驶这些Redis实例。

RedLock的意味着方法:

提供意味着所小时,以毫秒为单位。 按顺序排列向5个master路由注记乞求加吊。服务器端另设网络连接和响应延时小时,并且延时小时要相等吊的失效小时。(理论上吊启动时失效小时为10秒,则延时小时一般在5-50毫秒之间,我们就理论上延时小时是50ms吧)。如果延时,省去该master路由注记,及早去设法下一个master路由注记。 服务器端用于意味着所小时减去开始提供吊小时(即方法1就有的小时),得到提供吊用于的小时。当且仅当最多一半(N/2+1,这里是5/2+1=3个路由注记)的Redis master路由注记都赢取吊,并且用于的小时相等吊失效小时时,吊才算提供顺利。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms) 如果恰好了吊,key的真正有效小时就大变啦,所需减去提供吊所用于的小时。 如果提供吊不甘心(不了有在至少N/2+1个master实例恰好吊,有或者提供吊小时仍未最多了有效小时),服务器端要在所有的master路由注记上解吊(即便有些master路由注记究竟就不了有加吊顺利,也所需解吊,以防范有些漏网之鱼)。

简化下方法就是:

按顺序排列向5个master路由注记乞求加吊 根据另设的延时小时来适当,确实要省去该master路由注记。 如果少于相等3个路由注记加吊顺利,并且用于的小时相等吊的期限,亦可不作为加吊顺利啦。 如果提供吊不甘心,解吊!

Redisson意味着了redLock新版本的吊,有兴趣的小好朋友,可以去探究一下哈~

4. Zookeeper分布式吊

在进修Zookeeper分布式吊此前所,我们复习一下Zookeeper的路由注记哈。

Zookeeper的路由注记Znode有四种类型:

发挥作用路由注记:默认的路由注记类型。建立路由注记的服务器端与zookeeper断开连接后,该路由注记依旧长期存在。 发挥作用路由注记顺序排列路由注记:所谓顺序排列路由注记,就是在建立路由注记时,Zookeeper根据建立的小时顺序排列给该路由注记称呼同步进行编号,发挥作用路由注记顺序排列路由注记就是有顺序排列的发挥作用路由注记。 临时路由注记:和发挥作用路由注记相反,当建立路由注记的服务器端与zookeeper断开连接后,临时路由注记亦会被移除。 临时顺序排列路由注记:有顺序排列的临时路由注记。

Zookeeper分布式吊意味着运用于了临时顺序排列路由注记。这里不贴编译器啦,来讲下zk分布式吊的意味着原理吧。

4.1 zk提供吊过程

当第一个服务器端乞求上去时,Zookeeper服务器端亦会建立一个发挥作用路由注记locks。如果它(Client1)想赢取吊,所需在locks路由注记下建立一个顺序排列路由注记lock1.如图

接着,服务器端Client1亦会载入locks前面的所有临时顺序排列孙子路由注记,适当自己的路由注记lock1确实顺序排列最大者的那一个,如果是,则顺利赢取吊。

这时候如果又来一个服务器端client2前所来设法赢取吊,它亦会在locks下再建立一个临时路由注记lock2

服务器端client2一样也亦会载入locks前面的所有临时顺序排列孙子路由注记,适当自己的路由注记lock2确实最大者的,此时,辨认出lock1才是最大者的,于是提供吊不甘心。提供吊不甘心,它是不亦会甘心的,client2向它顺序排列靠前所的路由注记lock1注册Watcher事件,用来通话lock1到底长期存在,反之亦然client2抢吊不甘心转至等待状态。

此时,如果再来一个服务器端Client3来设法提供吊,它亦会在locks下再建立一个临时路由注记lock3

同样的,client3一样也亦会载入locks前面的所有临时顺序排列孙子路由注记,适当自己的路由注记lock3确实最大者的,发觉不是最大者的,就提供吊不甘心。它也是不亦会甘心的,它亦会向在它前所面的路由注记lock2注册Watcher事件,以通话lock2路由注记到底长期存在。

4.2 被囚吊

我们再来看看被囚吊的流程,Zookeeper的服务器端的业务未完毕成或者愈演愈烈系统故障,都亦会移除临时路由注记,被囚吊。如果是训练任务未完毕成,Client1亦会显式指令行移除lock1的指令

如果是服务器端系统故障了,根据临时路由注记得特性,lock1是亦会启动时移除的

lock1路由注记被移除后,Client2可老公了,因为它始终通话着lock1。lock1路由注记移除,Client2正要收到通知,也亦会载入locks前面的所有临时顺序排列孙子路由注记,发下lock2是最大者,就赢取吊。

全然,Client2赢取吊之后,Client3也对它盘踞,啊哈哈~

Zookeeper所设计定位就是分布式密切合作,恰当易用。如果提供足足吊,只需加进一个通话器亦可,很适合于做分布式吊。 Zookeeper作为分布式吊也缺点:如果有很多的服务器端频繁的申请加吊、被囚吊,对于Zookeeper战斗群的压力亦会相当大。 5. 三种分布式吊对比

5.1 数据源分布式吊意味着

优点:

恰当,用于方便,不所需应运而生Redis、zookeeper等中会间件。

缺点:

不适合于很低所发的桥段 db加载效率很差; 5.2 Redis分布式吊意味着

优点:

效率好,适合于很低所发桥段 较轻量级 有较好的开放性支持,如Redisson

缺点:

到期小时要好控制 所需重新考虑吊被别的多线程误删桥段 5.3 Zookeeper分布式吊意味着

缺点:

效率不如redis意味着的分布式吊 相当重的分布式吊。

优点:

有较好的效率和准确性 有封装较好的开放性,如Curator 5.4 对比汇总 从效率角度(从很低到偏很低)Redis> Zookeeper>= 数据源; 从理解的难易程度角度(从偏很低到很低)数据源> Redis> Zookeeper; 从意味着的技术性角度(从偏很低到很低)Zookeeper> Redis> 数据源; 从准确性角度(从很低到偏很低)Zookeeper> Redis> 数据源。。

广西白癜风医院排行
福州白癜风医院哪家专业好
贵阳妇科
吉林男科去哪看
天津妇科专科医院
小腹涨
老人抽搐
腹部疼
普通外科
止痒中药

上一篇: 三节放弃抵抗勇士控场?科尔微妙一贯埋隐患,独行侠狂投42记三分

下一篇: 有哪些好用的gif动图制作工具引荐?满满的干货!

友情链接