Redis分布式锁的正确实现方式

  • 时间:
  • 浏览:0
  • 来源:uu直播快3平台

*@paramrequestId请求标识

publicstaticvoidwrongGetLock1(Jedisjedis,StringlockKey,StringrequestId,intexpireTime){

错误示例1

Talk is cheap, show me the code。先展示代码,再带而且 人慢慢解释为哪此原先 实现:

具有容错性。假使 大次责的Redis节点正常运行,客户端就还前要加锁和解锁。

redis.clients

*@paramlockKey锁

Longresult=jedis.setnx(lockKey,requestId);

if(jedis.setnx(lockKey,expiresStr)==1){

publicstaticvoidwrongReleaseLock2(Jedisjedis,StringlockKey,StringrequestId){

加锁代码

publicstaticvoidwrongReleaseLock1(Jedisjedis,StringlockKey){

publicstaticbooleantryGetDistributedLock(Jedisjedis,StringlockKey,StringrequestId,intexpireTime){

returnfalse;

心细的童鞋就会发现了,而且 人的加锁代码满足而且 人可靠性里描述的一一兩个多多条件。首先,set()加入了NX参数,还前要保证是因为已有key位于,则函数不会调用成功,也然后我只一一兩个多多多客户端能持有锁,满足互斥性。其次,是因为而且 人对锁设置了过期时间,即使锁的持有者后续位于崩溃而没法解锁,锁也会是因为到了过期时间而自动解锁(即key被删除),不会位于死锁。

最常见的解锁代码然后我直接使用jedis.del()土措施删除锁,什儿 不先判断锁的拥有者而直接解锁的土措施,会是因为任何客户端都还前要随时进行解锁,即使这把锁删剪都是它的。

可靠性

互斥性。在任意时刻,只一一兩个多多多客户端能持有锁。

*尝试获取分布式锁

//是因为锁位于,获取锁的过期时间

最后,是因为而且 人将value赋值为requestId,代表加锁的客户端请求标识,没法在客户端在解锁的然后就还前要进行校验有无同一一兩个多多客户端。是因为而且 人只考虑Redis单机部署的场景,然后我容错性而且 人暂不考虑。

Objectresult=jedis.eval(script,Collections.singletonList(lockKey),Collections.singletonList(requestId));

}

}

returnfalse;

2.9.0

/**

publicclassRedisTool{

privatestaticfinalStringSET_WITH_EXPIRE_TIME="PX";

privatestaticfinalStringLOCK_SUCCESS="OK";

简单来说,然后我在eval命令执行Lua代码的然后,Lua代码将被当成一一兩个多多命令去执行,而且直到eval命令执行完成,Redis才会执行而且 命令。

如代码注释,问提在于是因为调用jedis.del()土措施的然后,这把锁是因为不属于当前客户端的以都会解除他人加的锁。没法有无真的有什儿 场景?答案是肯定的,比如客户端A加锁,一段时间然后客户端A解锁,在执行jedis.del()然后,锁时不时过期了,此时客户端B尝试加锁成功,而且客户端A再执行del()土措施,则将客户端B的锁给解除了。

*/

什儿 种错误示例就比较难以发现问提,而且实现也比较僵化 。实现思路:使用jedis.setnx()命令实现加锁,其中key是锁,value是锁的过期时间。

if(result==1){

执行过程:

}

}

首先而且 人要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:

首先,为了确保分布式锁可用,而且 人大慨要确保锁的实现一起去满足以下3个条件:

privatestaticfinalStringSET_IF_NOT_EXIST="NX";

还前要看到,而且 人解锁只前要两行代码就背熟了!第一行代码,而且 人写了一一兩个多多简单的Lua脚本代码,上一次见到什儿 编程语言还是在《黑客与画家》里,没想到这次青春恋爱物语用上了。第二行代码,而且 人将Lua代码传到jedis.eval()土措施里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()土措施是将Lua代码交给Redis服务端执行。

错误示例2

if(RELEASE_SUCCESS.equals(result)){

}

StringcurrentValueStr=jedis.get(lockKey);

不懂得问提都还前要在本群提出来 然后都会有直播平台和讲师直接交流噢

returntrue;

//而且 具体情况,一律返回加锁失败

解锁代码

*@paramjedisRedis客户端

returntrue;

jedis.del(lockKey);

//判断加锁与解锁是删剪都是同一一兩个多多客户端

jedis.expire(lockKey,expireTime);

publicclassRedisTool{

}

}

//若在这里进程时不时崩溃,则无法设置过期时间,将位于死锁

if(requestId.equals(jedis.get(lockKey))){

privatestaticfinalLongRELEASE_SUCCESS=1L;

}

//若在此时,这把锁时不时删剪都是什儿 客户端的,则会误解锁

正确姿势

总结

/**

*@return有无获取成功

*@paramjedisRedis客户端

}

longexpires=System.currentTimeMillis()+expireTime;

*@paramexpireTime超期时间

解铃还须系铃人。加锁和解锁前然后我同一一兩个多多客户端,客户端此人 能才能了把别人加的锁给解了。

本群提供免费的学习指导 架构资料 以及免费的解答

publicstaticbooleanwrongGetLock2(Jedisjedis,StringlockKey,intexpireTime){

还前要看到,而且 人加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),什儿 set()土措施一共有3个形参:

当前没法锁(key不位于),没法就进行加锁操作,并对锁设置个有效期,一起去value表示加锁的客户端。

错误示例2

}

publicstaticbooleanreleaseDistributedLock(Jedisjedis,StringlockKey,StringrequestId){

错误示例1

是因为想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的而且 人还前要加我的Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给而且 人。

本文主要介绍了咋样使用Java代码正我我随便说说现Redis分布式锁,对于加锁和解锁也分别给出了一一兩个多多比较经典的错误示例。我我随便说说让你通过Redis实现分布式锁越多难,假使 保证能满足可靠性里的3个条件。互联网我随便说说给而且 人带来了方便,假使 有问提就还前要google,然而网上的答案一定是对的吗?我我随便说说不然,然后我而且 人更应该时刻保持着质疑精神,多想多验证。

代码如下:

是因为锁是因为位于则获取锁的过期时间,和当前时间比较,是因为锁是因为过期,则设置新的过期时间,返回加锁成功。

分布式锁一般有什儿 实现土措施:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。本篇博客将介绍第二种土措施,基于Redis实现分布式锁。我随便说说网上是因为有各种介绍Redis分布式锁实现的博客,然而而且 人的实现却有着各种各样的问提,为了解决误人子弟,本篇博客将删剪介绍咋样正确地实现Redis分布式锁。

前言

setnx()土措施作用然后我SET IF NOT EXIST,expire()土措施然后我给锁加一一兩个多多过期时间。乍一看好像和前面的set()土措施结果一样,然而是因为这是两条Redis命令,不具有原子性,是因为进程在执行完setnx()然后时不时崩溃,是因为锁没法设置过期时间。没法是因为位于死锁。网上未必一群人原先 实现,是因为低版本的jedis越多支持多参数的set()土措施。

*@paramlockKey锁

returntrue;

第一一兩个多多为key,而且 人使用key来当锁,是因为key是唯一的。

欢迎工作一到八年的Java工程师而且 而且 人加入Java高级交流:787707172

*/

returnfalse;

Stringscript="ifredis.call('get',KEYS[1])==ARGV[1]thenreturnredis.call('del',KEYS[1])elsereturn0end";

正确姿势

jedis.del(lockKey);

//考虑多进程并发的具体情况,只一一兩个多多多进程的设置值和当前值相同,它才有权利加锁

if(LOCK_SUCCESS.equals(result)){

什儿 解锁代码乍一看也是没问提,甚至我然后也差点原先 实现,与正确姿势差越多,唯一区别的是分成两条命令去执行,代码如下:

第一一兩个多多为nxxx,什儿 参数而且 人填的是NX,意思是SET IF NOT EXIST,即当key不位于时,而且 人进行set操作;若key是因为位于,则不做任何操作;

*@return有无释放成功

}

第3个为expx,什儿 参数而且 人传的是PX,意思是而且 人要给什儿 key加一一兩个多多过期的设置,具体时间由第3个参数决定。

已有锁位于,不做任何操作。

通过setnx()土措施尝试加锁,是因为当前锁不位于,返回加锁成功。

*@paramrequestId请求标识

jedis

if(oldValueStr!=null&&oldValueStr.equals(currentValueStr)){

代码实现

第3个为value,而且 人传的是requestId,然后我童鞋是因为不明白,有key作为锁不就够哪年,为哪此前要用到value?是因为然后我而且 人在上面讲到可靠性时,分布式锁要满足第3个条件解铃还须系铃人,通过给value赋值为requestId,而且 人就知道这把锁是哪个请求加的了,在解锁的然后就还前要有土措施。requestId还前要使用UUID.randomUUID().toString()土措施生成。

}

returntrue;

StringexpiresStr=String.valueOf(expires);

没法这段代码问提在哪里?1. 是因为是客户端此人 生成过期时间,然后我前要强制要求分布式下每个客户端的时间前要同步。 2. 当锁过期的然后,是因为多个客户端一起去执行jedis.getSet()土措施,没法我随便说说最终只一一兩个多多多客户端还前要加锁,而且什儿 客户端的锁的过期时间是因为被而且 客户端覆盖。3. 锁不具备拥有者标识,即任何客户端都还前要解锁。

还是先展示代码,再带而且 人慢慢解释为哪此原先 实现:

组件依赖

if(currentValueStr!=null&&Long.parseLong(currentValueStr)

没法这段Lua代码的功能是哪此呢?我我随便说说很简单,首先获取锁对应的value值,检查有无与requestId相等,是因为相等则删除锁(解锁)。没法为哪此要使用Lua语言来实现呢?是因为要确保上述操作是原子性的。关于非原子性会带来哪此问提,还前要阅读【解锁代码-错误示例2】 。没法为哪此执行eval()土措施还前要确保原子性,源于Redis的特征,下面是官网对eval命令的次责解释:

Stringresult=jedis.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);

不会位于死锁。即使一一兩个多多多客户端在持有锁的期间崩溃而没法主动解锁,才能保证后续而且 客户端能加锁。

}

}

总的来说,执行上面的set()土措施就只会是因为什儿 结果:

//是因为当前锁不位于,返回加锁成功

StringoldValueStr=jedis.getSet(lockKey,expiresStr);

//锁已过期,获取上一一兩个多多锁的过期时间,并设置现在锁的过期时间

*释放分布式锁

比较常见的错误示例然后我使用jedis.setnx()和jedis.expire()组合实现加锁,代码如下:

第3个为time,与第3个参数相呼应,代表key的过期时间。

}