游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

用MySQL实现分布式锁,你听过吗?

  概述
  以前参加过一个库存系统,由于其业务复杂性,搞了很多个应用来支撑。这样的话一份库存数据就有可能同时有多个应用来修改库存数据。
  比如说,有定时任务域xx。cron,和SystemA域和SystemB域这几个JAVA应用,可能同时修改同一份库存数据。如果不做协调的话,就会有脏数据出现。
  对于跨JAVA进程的线程协调,可以借助外部环境,例如DB或者Redis。下文介绍一下如何使用DB来实现分布式锁。设计
  本文设计的分布式锁的交互方式如下:根据业务字段生成transactionid,并线程安全的创建锁资源根据transactionid申请锁释放锁动态创建锁资源
  在使用synchronized关键字的时候,必须指定一个锁对象。synchronized(obj){}
  进程内的线程可以基于obj来实现同步。obj在这里可以理解为一个锁对象。如果线程要进入synchronized代码块里,必须先持有obj对象上的锁。这种锁是JAVA里面的内置锁,创建的过程是线程安全的。那么借助DB,如何保证创建锁的过程是线程安全的呢?
  可以利用DB中的UNIQUEKEY特性,一旦出现了重复的key,由于UNIQUEKEY的唯一性,会抛出异常的。在JAVA里面,是SQLIntegrityConstraintViolationException异常。createtabledistributedlock(idBIGINTUNSIGNEDPRIMARYKEYAUTOINCREMENTCOMMENT自增主键,transactionidvarchar(128)NOTNULLDEFAULTCOMMENT事务id,lastupdatetimeTIMESTAMPDEFAULTCURRENTTIMESTAMPONUPDATECURRENTTIMESTAMPNOTNULLCOMMENT最后更新时间,createtimeTIMESTAMPDEFAULT0000000000:00:00NOTNULLCOMMENT创建时间,UNIQUEKEYidxtransactionid(transactionid))
  transactionid是事务Id,比如说,可以用
  仓库条码销售模式
  来组装一个transactionid,表示某仓库某销售模式下的某个条码资源。不同条码,当然就有不同的transactionid。如果有两个应用,拿着相同的transactionid来创建锁资源的时候,只能有一个应用创建成功。
  一条distributedlock记录插入成功了,就表示一份锁资源创建成功了。DB连接池列表设计
  在写操作频繁的业务系统中,通常会进行分库,以降低单数据库写入的压力,并提高写操作的吞吐量。如果使用了分库,那么业务数据自然也都分配到各个数据库上了。
  在这种水平切分的多数据库上使用DB分布式锁,可以自定义一个DataSouce列表。并暴露一个getConnection(StringtransactionId)方法,按照transactionId找到对应的Connection。
  实现代码如下:packagedlock;importcom。alibaba。druid。pool。DruidDataSource;importorg。springframework。stereotype。Component;importjavax。annotation。PostConstruct;importjava。io。FileInputStream;importjava。io。IOException;importjava。sql。Connection;importjava。util。ArrayList;importjava。util。List;importjava。util。Properties;ComponentpublicclassDataSourcePool{privateListDruidDataSourcedlockDataSourcesnewArrayList();PostConstructprivatevoidinitDataSourceList()throwsIOException{PropertiespropertiesnewProperties();FileInputStreamfisnewFileInputStream(db。properties);properties。load(fis);IntegerlockNumInteger。valueOf(properties。getProperty(DLOCKNUM));for(inti0;ilockNum;i){Stringuserproperties。getProperty(DLOCKUSERi);Stringpasswordproperties。getProperty(DLOCKPASSi);IntegerinitSizeInteger。valueOf(properties。getProperty(DLOCKINITSIZEi));IntegermaxSizeInteger。valueOf(properties。getProperty(DLOCKMAXSIZEi));Stringurlproperties。getProperty(DLOCKURLi);DruidDataSourcedataSourcecreateDataSource(user,password,initSize,maxSize,url);dlockDataSources。add(dataSource);}}privateDruidDataSourcecreateDataSource(Stringuser,Stringpassword,IntegerinitSize,IntegermaxSize,Stringurl){DruidDataSourcedataSourcenewDruidDataSource();dataSource。setDriverClassName(com。mysql。jdbc。Driver);dataSource。setUsername(user);dataSource。setPassword(password);dataSource。setUrl(url);dataSource。setInitialSize(initSize);dataSource。setMaxActive(maxSize);returndataSource;}publicConnectiongetConnection(StringtransactionId)throwsException{if(dlockDataSources。size()0){returnnull;}if(transactionIdnull。equals(transactionId)){thrownewRuntimeException(transactionId是必须的);}inthascodetransactionId。hashCode();if(hascode0){hascodehascode;}returndlockDataSources。get(hascodedlockDataSources。size())。getConnection();}}
  首先编写一个initDataSourceList方法,并利用Spring的PostConstruct注解初始化一个DataSource列表。相关的DB配置从db。properties读取。DLOCKNUM2DLOCKUSER0user1DLOCKPASS0pass1DLOCKINITSIZE02DLOCKMAXSIZE010DLOCKURL0jdbc:mysql:localhost:3306test1DLOCKUSER1user1DLOCKPASS1pass1DLOCKINITSIZE12DLOCKMAXSIZE110DLOCKURL1jdbc:mysql:localhost:3306test2
  DataSource使用阿里的DruidDataSource。
  接着最重要的一个实现getConnection(StringtransactionId)方法。实现原理很简单,获取transactionId的hashcode,并对DataSource的长度取模即可。
  连接池列表设计好后,就可以实现往distributedlock表插入数据了。packagedlock;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。stereotype。Component;importjava。sql。;ComponentpublicclassDistributedLock{AutowiredprivateDataSourcePooldataSourcePool;根据transactionId创建锁资源publicStringcreateLock(StringtransactionId)throwsException{if(transactionIdnull){thrownewRuntimeException(transactionId是必须的);}Connectionconnectionnull;Statementstatementnull;try{connectiondataSourcePool。getConnection(transactionId);connection。setAutoCommit(false);statementconnection。createStatement();statement。executeUpdate(INSERTINTOdistributedlock(transactionid)VALUES(transactionId));connection。commit();returntransactionId;}catch(SQLIntegrityConstraintViolationExceptionicv){说明已经生成过了。if(connection!null){connection。rollback();}returntransactionId;}catch(Exceptione){if(connection!null){connection。rollback();}throwe;}finally{if(statement!null){statement。close();}if(connection!null){connection。close();}}}}根据transactionId锁住线程
  接下来利用DB的selectforupdate特性来锁住线程。当多个线程根据相同的transactionId并发同时操作selectforupdate的时候,只有一个线程能成功,其他线程都block住,直到selectforupdate成功的线程使用commit操作后,block住的所有线程的其中一个线程才能开始干活。
  我们在上面的DistributedLock类中创建一个lock方法。publicbooleanlock(StringtransactionId)throwsException{Connectionconnectionnull;PreparedStatementpreparedStatementnull;ResultSetresultSetnull;try{connectiondataSourcePool。getConnection(transactionId);preparedStatementconnection。prepareStatement(SELECTFROMdistributedlockWHEREtransactionid?FORUPDATE);preparedStatement。setString(1,transactionId);resultSetpreparedStatement。executeQuery();if(!resultSet。next()){connection。rollback();returnfalse;}returntrue;}catch(Exceptione){if(connection!null){connection。rollback();}throwe;}finally{if(preparedStatement!null){preparedStatement。close();}if(resultSet!null){resultSet。close();}if(connection!null){connection。close();}}}实现解锁操作
  当线程执行完任务后,必须手动的执行解锁操作,之前被锁住的线程才能继续干活。在我们上面的实现中,其实就是获取到当时selectforupdate成功的线程对应的Connection,并实行commit操作即可。
  那么如何获取到呢?我们可以利用ThreadLocal。首先在DistributedLock类中定义privateThreadLocalConnectionthreadLocalConnnewThreadLocal();
  每次调用lock方法的时候,把Connection放置到ThreadLocal里面。我们修改lock方法。publicbooleanlock(StringtransactionId)throwsException{Connectionconnectionnull;PreparedStatementpreparedStatementnull;ResultSetresultSetnull;try{connectiondataSourcePool。getConnection(transactionId);threadLocalConn。set(connection);preparedStatementconnection。prepareStatement(SELECTFROMdistributedlockWHEREtransactionid?FORUPDATE);preparedStatement。setString(1,transactionId);resultSetpreparedStatement。executeQuery();if(!resultSet。next()){connection。rollback();threadLocalConn。remove();returnfalse;}returntrue;}catch(Exceptione){if(connection!null){connection。rollback();threadLocalConn。remove();}throwe;}finally{if(preparedStatement!null){preparedStatement。close();}if(resultSet!null){resultSet。close();}if(connection!null){connection。close();}}}
  这样子,当获取到Connection后,将其设置到ThreadLocal中,如果lock方法出现异常,则将其从ThreadLocal中移除掉。
  有了这几步后,我们可以来实现解锁操作了。我们在DistributedLock添加一个unlock方法。publicvoidunlock()throwsException{Connectionconnectionnull;try{connectionthreadLocalConn。get();if(!connection。isClosed()){connection。commit();connection。close();threadLocalConn。remove();}}catch(Exceptione){if(connection!null){connection。rollback();connection。close();}threadLocalConn。remove();throwe;}}缺点
  毕竟是利用DB来实现分布式锁,对DB还是造成一定的压力。当时考虑使用DB做分布式的一个重要原因是,我们的应用是后端应用,平时流量不大的,反而关键的是要保证库存数据的正确性。对于像前端库存系统,比如添加购物车占用库存等操作,最好别使用DB来实现分布式锁了。进一步思考
  如果想锁住多份数据该怎么实现?比如说,某个库存操作,既要修改物理库存,又要修改虚拟库存,想锁住物理库存的同时,又锁住虚拟库存。其实也不是很难,参考lock方法,写一个multiLock方法,提供多个transactionId的入参,for循环处理就可以了。这个后续有时间再补上。

非营利性咨询公司Onward视觉形象设计Onward成立于2017年,是一家小型的非营利性咨询公司。他们与公司、非营利性机构、学校系统以及社区合作,建立个人和组织变革的能力。服务包括组织战略、高管培训计划、学习机会以……大众ID。3比亚迪海豚领衔,10款成都车展纯电新车抢先看成都车展已于今日开展,众多新车集中亮相,一时让人眼花缭乱。其中,众多纯电动新车的亮相尤其惹眼,包括关注度较高的比亚迪海豚、比亚迪元PLUS、大众ID。3、欧拉复古系列,以及造车……小时候街角的美食回忆,台湾庶民小吃香菇油饭油饭是台湾的庶民小吃,我家街角就有一家卖油饭的饭馆。小时候我和姐姐简直每天都要去吃一碗油饭。油饭的品种也是形形色色,有花生油饭、猪脚油饭、海鲜油饭等等。今天在《詹姆士的厨房》我……线上化不是解决一切问题的灵丹妙药,需要用数字化科技去赋能门店谈到疫情对于人工智能与数字化领域发展的影响,悠络客COO邹一波认为,对于未来,疫情是一个非常重要的时间节点,从行业整体来看,我身边很多资深业内人士均认为不要低估疫情的影响,毫不……共享电动自行车HumanForest视觉形象设计HumanForest于2020年推出,是英国首个免费共享电动自行车计划,目标是减少城市的二氧化碳排放量,使之成为一个绿色,健康的工作,生活和娱乐场所。在最初的推广期间,必须在……强田液压联手中望软件,共推国产液压系统自主化发展液压系统主要是为各类机器传递动力,相当于说,从发动机或者电机通过什么介质将动能传输到执行机构,实现这种直线运动和旋转运动,液压件就是一种介质。上海强田液压股份有限公司(以下简称……0元购iPhone12套餐来了,0元购Mate40套餐呢?三大电信运营商的财报显示,2020年前三季度共实现营收约10924亿元,其中中国移动实现营收5744亿元,中国电信和中国联通分别实现营收2926亿元和2254亿元,两家合计营收……被骗了!红米K40比吴亦凡还渣红米K40的手机虽然很强,性价比很高,当时也是看中了这些,忍不住在618之前就买了它。但随着我收到实机到现在过去了两个多月了,一些问题也随之而来,尤其是这几点,让我苦红米K40……拯救五音不全唱吧G1无线喇叭麦克风开箱在值得买太原分舵的一次线下聚会,吃完饭大家正琢磨着去哪儿接着浪,站内红人缘溪行神神叨叨说要给我们看看她的新玩具,然后就拿出了她粉红色的小宝贝一个话筒。话筒有什么稀罕,谁还……升级macOS10。15。4软件不兼容怎么办?软件意外退出解最新系统10。15。4部分软件无法启动,会存在闪退或崩溃的问题,怎么办?下面macw小编为大家带来升级macOS10。15。4软件不兼容解决方法。第一步安装Xcode,如……紧急情况不要怕卡儿酷应急电源帮您忙如今社会快速发展,越来越多的家庭都开上了自己家的小轿车。而大家在路上行驶的时候难免会遇到汽车抛锚打不着火的情况。如果这种事情发生在市区并且在白天的话其实也还好,但是如果它发生在……对标冠道,15万出头买大五座中型SUV,轴距近2。9米,标配七座中型SUV应该是目前最受欢迎的车型,但要是论舒适性,还是大五座更出色,在这个领域,本田冠道算是做得不错的,不过20多万买一台搭载1。5TCVT的车型,想必也跟性价比没啥联系……
名门修谱家谱是一本不可多得的财富秘籍互联网家谱国史,方志,家谱三驾马车并驶,传承中国几千年文明发展史。而家谱作为一直深藏民间最贴近民间百姓的家族文化,每一次新修不单单是表现对祖先光辉历史的尊敬,更是表达了对……不完美,但是值得拥有JEET蓝牙耳机,一周体验运动耳机是大部分喜欢运动人士的必备产品,舒适的佩戴感受,还有动感的音乐陪伴运动锻炼,健身更专注而不会受到外界噪音的干扰。泰捷近期也推出了自己的一款蓝牙耳机,很多朋友一定听说过或……古装剧长相守热播周雪菲拍前看原版小说走进角色内心星关系5月22日讯近日,由于小彤、关智斌、毛晓慧、楷旋、周雪菲等联袂出演的古装励志成长大剧《长相守》正在优酷、爱奇艺、腾讯视频热播,该剧自开播以来,因其跌宕起伏的剧情受到广大网……时间都去哪了?英媒被手机偷走了据英国广播公司网站1月12日报道,据美国移动应用监测公司安亿致用公司统计,人们平均每天花在手机上的时间为4。8小时。报道称,2020年,英国监管机构英国通信管理局也发现人……节冰新剧重庆谈判杀青老戏骨全新演绎致敬历史星关系10月23日讯在曝光的几张剧照中,节冰一身符合时代感的中山装造型,一副复古的黑色圆框眼镜,跟历史人物相贴合的背头发型也是十分经典。双手背后的站姿,一脸严肃的表情,眼神中透……演员节冰新剧重庆谈判火热开拍网友期待老戏骨的精彩演出星关系9月3日讯近日,由演员节冰参演的历史题材电视剧《重庆谈判》正在重庆火热拍摄中。节冰在该剧中饰演著名实业家吴鼎昌。在曝光的几张剧照中,节冰造型充满时代感。大背头的发型……热血高校开播蒋雪鸣版中二暖男上线《热血高校》开播蒋雪鸣版中二暖男上线由恺英网络出品,许肇任执导,改编自经典红白机《热血高校》IP的运动题材青春剧《热血高校》高燃开播,青年演员蒋雪鸣饰演男一号袁非,引发粉……iOS15。0。1开放更新!修复AppleWatch解锁iP在iOS15开放更新十天后,美国Apple公司今天发布了iOS15。0。1更新,修复了AppleWatch解锁iPhone13机型上的错误,和设定App可能错误地显示储存空间已……机智过人聚焦科技生活韩雪亲自上台参加挑战星关系8月12日讯由中央广播电视总台、中国科学院和工业与信息化部共同主办,中央广播电视总台央视综合频道、中国科学院科学传播局和北京长江文化股份有限公司联合制作的大型科技挑战类节……电视剧陪读妈妈将播梅婷邬君梅曾黎郝洋陪读妈妈帮诠释别样陪读风电视剧《陪读妈妈》知名导演陈畅执导的国内首部海外陪读题材剧,剧中梅婷、邬君梅、曾黎、郝洋饰演的陪读妈妈性格、职业各不相同的四位妈妈因同在温哥华陪读而相遇相识,再加几位实力派演员……鸿蒙系统开源会议纪要OpenHarmony架构SIG纪要20议题一:申请新建开源仓:thirdpartycef李征结论:同意新建thirdpartycef仓,基于ChromiumEmbeddedFramework(CEF)构……阿里菜鸟网络强势删除自媒体文章,将快递价格战归咎于极兔快递这一届阿里菜鸟的公关也是醉了,指名道姓说极兔造成快递价格战作为一个快递平台,菜鸟并无理由针对极兔快递,自有更深入的原因2021年9月,本自媒体的文章多次被阿里巴巴菜……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网