幼儿饰品瑜伽美体用品美文
快百科
美文创意
爱情通信
用品婚姻
爱好看病
美体软件
影音星座
瑜伽周边
星座办公
饰品塑形
搞笑减肥
幼儿两性
智家潮品

高并发没锁可不行,三种分布式锁详解

11月7日 亡命徒投稿
  Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境如果我们需要加锁就显得无能为力。在单个JVM实例上,锁的竞争者通常是一些不同的线程,而在分布式环境中,锁的竞争者通常是一些不同的线程或者进程。如何实现在分布式环境中对一个对象进行加锁呢?答案就是分布式锁。分布式锁实现方案
  目前分布式锁的实现方案主要包括三种:基于数据库(唯一索引)基于缓存(Redis,memcached,tair)基于Zookeeper
  基于数据库实现分布式锁主要是利用数据库的唯一索引来实现,唯一索引天然具有排他性,这刚好符合我们对锁的要求:同一时刻只能允许一个竞争者获取锁。加锁时我们在数据库中插入一条锁记录,利用业务id进行防重。当第一个竞争者加锁成功后,第二个竞争者再来加锁就会抛出唯一索引冲突,如果抛出这个异常,我们就判定当前竞争者加锁失败。防重业务id需要我们自己来定义,例如我们的锁对象是一个方法,则我们的业务防重id就是这个方法的名字,如果锁定的对象是一个类,则业务防重id就是这个类名。
  基于缓存实现分布式锁:理论上来说使用缓存来实现分布式锁的效率最高,加锁速度最快,因为Redis几乎都是纯内存操作,而基于数据库的方案和基于Zookeeper的方案都会涉及到磁盘文件IO,效率相对低下。一般使用Redis来实现分布式锁都是利用Redis的SETNXkeyvalue这个命令,只有当key不存在时才会执行成功,如果key已经存在则命令执行失败。
  基于Zookeeper:Zookeeper一般用作配置中心,其实现分布式锁的原理和Redis类似,我们在Zookeeper中创建瞬时节点,利用节点不能重复创建的特性来保证排他性。
  在实现分布式锁的时候我们需要考虑一些问题,例如:分布式锁是否可重入,分布式锁的释放时机,分布式锁服务端是否有单点问题等。基于数据库实现分布式锁
  上面已经分析了基于数据库实现分布式锁的基本原理:通过唯一索引保持排他性,加锁时插入一条记录,解锁是删除这条记录。下面我们就简要实现一下基于数据库的分布式锁。表设计CREATETABLEdistributedlock(idbigint(20)NOTNULLAUTOINCREMENT,uniquemutexvarchar(255)NOTNULLCOMMENT业务防重id,holderidvarchar(255)NOTNULLCOMMENT锁持有者id,createtimedatetimeDEFAULTNULLONUPDATECURRENTTIMESTAMP,PRIMARYKEY(id),UNIQUEKEYmutexindex(uniquemutex))ENGINEInnoDBDEFAULTCHARSETutf8;
  id字段是数据库的自增id,uniquemutex字段就是我们的防重id,也就是加锁的对象,此对象唯一。在这张表上我们加了一个唯一索引,保证uniquemutex唯一性。holderid代表竞争到锁的持有者id。加锁insertintodistributedlock(uniquemutex,holderid)values(uniquemutex,holderid);
  如果当前sql执行成功代表加锁成功,如果抛出唯一索引异常(DuplicatedKeyException)则代表加锁失败,当前锁已经被其他竞争者获取。解锁deletefrommethodL
  解锁很简单,直接删除此条记录即可。分析
  是否可重入:就以上的方案来说,我们实现的分布式锁是不可重入的,即是是同一个竞争者,在获取锁后未释放锁之前再来加锁,一样会加锁失败,因此是不可重入的。解决不可重入问题也很简单:加锁时判断记录中是否存在uniquemutex的记录,如果存在且holderid和当前竞争者id相同,则加锁成功。这样就可以解决不可重入问题。
  锁释放时机:设想如果一个竞争者获取锁时候,进程挂了,此时distributedlock表中的这条记录就会一直存在,其他竞争者无法加锁。为了解决这个问题,每次加锁之前我们先判断已经存在的记录的创建时间和当前系统时间之间的差是否已经超过超时时间,如果已经超过则先删除这条记录,再插入新的记录。另外在解锁时,必须是锁的持有者来解锁,其他竞争者无法解锁。这点可以通过holderid字段来判定。
  数据库单点问题:单个数据库容易产生单点问题:如果数据库挂了,我们的锁服务就挂了。对于这个问题,可以考虑实现数据库的高可用方案,例如MySQL的MHA高可用解决方案。基于缓存实现分布式锁,以Redis为例
  使用Jedis来和Redis通信。加锁publicclassRedisTool{privatestaticfinalStringLOCKSUCCESSOK;privatestaticfinalStringSETIFNOTEXISTNX;privatestaticfinalStringSETWITHEXPIRETIMEPX;加锁paramjedisRedis客户端paramlockKey锁的keyparamrequestId竞争者idparamexpireTime锁超时时间,超时之后锁自动释放returnpublicstaticbooleangetDistributedLock(Jedisjedis,StringlockKey,StringrequestId,intexpireTime){Stringresultjedis。set(lockKey,requestId,SETIFNOTEXIST,SETWITHEXPIRETIME,expireTime);returnOK。equals(result);}}
  可以看到,我们加锁就一行代码:
  jedis。set(Stringkey,Stringvalue,Stringnxxx,Stringexpx,inttime);
  这个set()方法一共五个形参:
  第一个为key,我们使用key来当锁,因为key是唯一的。
  第二个为value,这里写的是锁竞争者的id,在解锁时,我们需要判断当前解锁的竞争者id是否为锁持有者。
  第三个为nxxx,这个参数我们填的是NX,意思是SETIFNOTEXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作。
  第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期时间的设置,具体时间由第五个参数决定;
  第五个参数为time,与第四个参数相呼应,代表key的过期时间。
  总的来说,执行上面的set()方法就只会导致两种结果:1。当前没有锁(key不存在),那么久进行加锁操作,并对锁设置一个有效期,同时value表示加锁的客户端。2。已经有锁存在,不做任何操作。
  上述解锁请求中,SETIFNOTEXIST(不存在则执行)保证了加锁请求的排他性,缓存超时机制保证了即使一个竞争者加锁之后挂了,也不会产生死锁问题:超时之后其他竞争者依然可以获取锁。通过设置value为竞争者的id,保证了只有锁的持有者才能来解锁,否则任何竞争者都能解锁,那岂不是乱套了。解锁publicclassRedisTool{privatestaticfinalLongRELEASESUCCESS1L;释放分布式锁paramjedisRedis客户端paramlockKey锁paramrequestId锁持有者idreturn是否释放成功publicstaticbooleanreleaseDistributedLock(Jedisjedis,StringlockKey,StringrequestId){Stringscriptifredis。call(get,KEYS〔1〕)ARGV〔1〕thenreturnredis。call(del,KEYS〔1〕)elsereturn0Objectresultjedis。eval(script,Collections。singletonList(lockKey),Collections。singletonList(requestId));returnRELEASESUCCESS。equals(result);}}
  解锁的步骤:判断当前解锁的竞争者id是否为锁的持有者,如果不是直接返回失败,如果是则进入第2步。删除key,如果删除成功,返回解锁成功,否则解锁失败。
  注意到这里解锁其实是分为2个步骤,涉及到解锁操作的一个原子性操作问题。这也是为什么我们解锁的时候用Lua脚本来实现,因为Lua脚本可以保证操作的原子性。那么这里为什么需要保证这两个步骤的操作是原子操作呢?
  设想:假设当前锁的持有者是竞争者1,竞争者1来解锁,成功执行第1步,判断自己就是锁持有者,这是还未执行第2步。这是锁过期了,然后竞争者2对这个key进行了加锁。加锁完成后,竞争者1又来执行第2步,此时错误产生了:竞争者1解锁了不属于自己持有的锁。可能会有人问为什么竞争者1执行完第1步之后突然停止了呢?这个问题其实很好回答,例如竞争者1所在的JVM发生了GC停顿,导致竞争者1的线程停顿。这样的情况发生的概率很低,但是请记住即使只有万分之一的概率,在线上环境中完全可能发生。因此必须保证这两个步骤的操作是原子操作。分析
  是否可重入:以上实现的锁是不可重入的,如果需要实现可重入,在SETIFNOTEXIST之后,再判断key对应的value是否为当前竞争者id,如果是返回加锁成功,否则失败。
  锁释放时机:加锁时我们设置了key的超时,当超时后,如果还未解锁,则自动删除key达到解锁的目的。如果一个竞争者获取锁之后挂了,我们的锁服务最多也就在超时时间的这段时间之内不可用。
  Redis单点问题:如果需要保证锁服务的高可用,可以对Redis做高可用方案:Redis集群主从切换。目前都有比较成熟的解决方案。基于Zookeeper实现分布式锁加锁和解锁流程
  利用Zookeeper创建临时有序节点来实现分布式锁:当一个客户端来请求时,在锁的空间下面创建一个临时有序节点。如果当前节点的序列是这个空间下面最小的,则代表加锁成功,否则加锁失败,加锁失败后设置Watcher,等待前面节点的通知。当前节点监听其前面一个节点,如果前面一个节点删除了就通知当前节点。当解锁时当前节点通知其后继节点,并删除当前节点。
  其基本思想类似于AQS中的等待队列,将请求排队处理。其流程图如下:
  分析
  解决不可重入:客户端加锁时将主机和线程信息写入锁中,下一次再来加锁时直接和序列最小的节点对比,如果相同,则加锁成功,锁重入。
  锁释放时机:由于我们创建的节点是顺序临时节点,当客户端获取锁成功之后突然session会话断开,ZK会自动删除这个临时节点。
  单点问题:ZK是集群部署的,主要一半以上的机器存活,就可以保证服务可用性。利用curator实现
  Zookeeper第三方客户端curator中已经实现了基于Zookeeper的分布式锁。利用curator加锁和解锁的代码如下:加锁,支持超时,可重入publicbooleantryLock(longtimeout,TimeUnitunit)throwsInterruptedException{try{returninterProcessMutex。acquire(timeout,unit);}catch(Exceptione){e。printStackTrace();}}解锁publicbooleanunlock(){try{interProcessMutex。release();}catch(Throwablee){log。error(e。getMessage(),e);}finally{executorService。schedule(newCleaner(client,path),delayTimeForClean,TimeUnit。MILLISECONDS);}}三种方案比较
投诉 评论 转载

小米12S性能强悍,小米Civi1S开始超低价热销,不能再等小米Civi1S是一款为年轻人设计的时尚机型。渐变的机身设计让手机的颜值更加丰富多彩,正面加持6。55英寸有机发光二极管柔性屏,支持120Hz刷新率。在10位原色配置下,手机呈……21世纪五大联赛俱乐部帽子戏法榜单梅西C罗再次霸榜21世纪五大联赛俱乐部帽子戏法榜单:梅罗再次霸榜,莱万紧随其后近日,德转统计了一组欧洲五大联赛球员的一组数据,这组数据是从二十一世纪开始的。新赛季以来有无数球员曾经在五大……担忧!英超球星别墅遭山火侵袭,妻子和五个孩子被困,水源告急山火正在葡萄牙等欧洲国家肆虐,一些名人不得不从他们位于山上的豪宅中紧急疏散。目前,莱斯特城前锋瓦尔迪的妻子正带着五个孩子在葡萄牙度假,他们的别墅被山火包围,瓦尔迪妻子正在做紧急……12GB256GB5000mAh,现已跌至1799元,顶配旗大品牌值得信赖,这是曾经非常流行的一句广告语,因为言简意赅又直击痛点,所以也被誉为是广告业的一个经典,这句话非常直接的点明了品牌的重要性,大品牌不像小作坊,需要对每一款产品负责……经典语录十则1。躺在成就上就像行进时在雪地里一样危险,你昏昏沉沉,在熟睡中死去。2。成功并不能用一个人达到什么地位来衡量,而是依据他在迈向成功的过程中,到底克服了多少困难和障碍。……苹果AppStore精选iOS限免软件领取啦(4。26)限免App说明1、APP有自己的限免时间,可根据自己的需求及时下载,若显示仍然收费请不要下载,表示该应用限免时间结束。2、图标呈云状,表示该应用已经被你获取,即该应……高并发没锁可不行,三种分布式锁详解Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境如果我们需要加锁就显得无能为力。在单个JVM实例上,锁的竞争……中国7款光瓶酒,100纯粮发酵,好喝不贵,懂酒的常喝平时咱老百姓喝酒,要的就是个实惠,再好看的酒瓶,再华丽的包装,买回去也进不了肚子不是?更何况,花里胡哨的东西大多会华而不实。酒嘛,好喝就行,包装有时候真的没那么重要。……回忆珍藏!汉印CP4000L照片打印机体验前段时间朋友在求婚的时候,拍了很多的照片,然后在网上找人帮忙打印出来,给别人多少钱一张。照片打印出来之后,他就跑来找我吐槽说被坑了。因为他原本计划是把照片打印出来做个相册集的,……太空漂流7年后,SpaceX的火箭残骸或在3月撞月球负责推算火箭撞月球碰撞过程的专家预测,美国太空探索技术公司(SpaceX)的猎鹰9号助推器在太空漂流7年后,将在2022年3月撞上月球。图为英国广播公司(BBC)报道截图……三次大赛小枣为何落泪刘诗雯坎坷追梦路(诗四首)2015世乒赛苏州世乒进决赛,小枣丁宁争金冠;生死存亡决胜局,小枣领先出意外。丁宁崴脚求医治,小枣懵圈……保价27天免退货运费等,今年双11这些规则改了明天(10月20日)起,今年双11预售正式开启,电商平台也陆续公布了新玩法。问题来了,你的钱包准备好了吗?告别零点依旧考验体力双11原本是个为期1天的电商促销活动,……
毛纳欢歌奔现记农旅文旅茶旅!毛纳村企1867公司的乡村振兴探三大运营商1月成绩单出炉5G套餐用户总数破11亿华为P60外观细节现身,全新旗舰你还期待哪些升级?队长朱婷正式发声,回归中国女排进入倒计时,蔡斌振奋,球迷欢呼泰安市徂汶景区徂徕镇梨花开迎客来动漫情头盐于律己,甜以待人百亿补贴亮剑,奥特莱斯狂飙三星Exynos2300规格曝光,GalaxyZFold5或卡兰加离婚官司缠身无法回归,河南队已做最坏打算钱天一好样的!41大胜波尔卡诺娃率先闯进四强!预测2023年选秀前十顺位之610顺位14投14中!保罗打破NBA季后赛纪录!他依然是控卫之神
带着坚强出发最后一个秋夜,我与雪花有约夜钓鲢鳙最佳时间(晚上钓白鲢好钓吗)热闻聚热点网 孕妇羊水少是为什么羊水少有什么症状全球首款RISCV笔记本ROMA正式发布!老人吃洋葱保健更轻松爱弥儿600字读后感如何挑选泳帽的尺寸义务教育阶段为何不宜举办学科类竞赛活动中秋日泛湖杂诗其七抗日英雄谢晋元的故事(简介)资金管理工作总结

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找漯河衢州兴义眉山桂林阳泉玉溪简阳山东遂宁永城新余梧州洛阳泸州温州临汾清远营口常熟浙江大连桐乡宜昌