范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

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

  Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境如果我们需要加锁就显得无能为力。在单个JVM实例上,锁的竞争者通常是一些不同的线程,而在分布式环境中,锁的竞争者通常是一些不同的线程或者进程。如何实现在分布式环境中对一个对象进行加锁呢?答案就是分布式锁。 分布式锁实现方案
  目前分布式锁的实现方案主要包括三种: 基于数据库(唯一索引) 基于缓存(Redis,memcached,tair) 基于Zookeeper
  基于数据库实现分布式锁主要是利用数据库的唯一索引来实现,唯一索引天然具有排他性,这刚好符合我们对锁的要求:同一时刻只能允许一个竞争者获取锁。加锁时我们在数据库中插入一条锁记录,利用业务id进行防重。当第一个竞争者加锁成功后,第二个竞争者再来加锁就会抛出唯一索引冲突,如果抛出这个异常,我们就判定当前竞争者加锁失败。防重业务id需要我们自己来定义,例如我们的锁对象是一个方法,则我们的业务防重id就是这个方法的名字,如果锁定的对象是一个类,则业务防重id就是这个类名。
  基于缓存实现分布式锁:理论上来说使用缓存来实现分布式锁的效率最高,加锁速度最快,因为Redis几乎都是纯内存操作,而基于数据库的方案和基于Zookeeper的方案都会涉及到磁盘文件IO,效率相对低下。一般使用Redis来实现分布式锁都是利用Redis的 SETNX key value  这个命令,只有当key不存在时才会执行成功,如果key已经存在则命令执行失败。
  基于Zookeeper:Zookeeper一般用作配置中心,其实现分布式锁的原理和Redis类似,我们在Zookeeper中创建瞬时节点,利用节点不能重复创建的特性来保证排他性。
  在实现分布式锁的时候我们需要考虑一些问题,例如:分布式锁是否可重入,分布式锁的释放时机,分布式锁服务端是否有单点问题等。 基于数据库实现分布式锁
  上面已经分析了基于数据库实现分布式锁的基本原理:通过唯一索引保持排他性,加锁时插入一条记录,解锁是删除这条记录。下面我们就简要实现一下基于数据库的分布式锁。 表设计CREATE TABLE `distributed_lock` (   `id` bigint(20) NOT NULL AUTO_INCREMENT,   `unique_mutex` varchar(255) NOT NULL COMMENT "业务防重id",   `holder_id` varchar(255) NOT NULL COMMENT "锁持有者id",   `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,   PRIMARY KEY (`id`),   UNIQUE KEY `mutex_index` (`unique_mutex`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  id字段是数据库的自增id,unique_mutex字段就是我们的防重id,也就是加锁的对象,此对象唯一。在这张表上我们加了一个唯一索引,保证unique_mutex唯一性。holder_id代表竞争到锁的持有者id。 加锁insert into distributed_lock(unique_mutex, holder_id) values ("unique_mutex", "holder_id");
  如果当前sql执行成功代表加锁成功,如果抛出唯一索引异常(DuplicatedKeyException)则代表加锁失败,当前锁已经被其他竞争者获取。 解锁delete from methodLock where unique_mutex="unique_mutex" and holder_id="holder_id";
  解锁很简单,直接删除此条记录即可。 分析
  是否可重入 :就以上的方案来说,我们实现的分布式锁是不可重入的,即是是同一个竞争者,在获取锁后未释放锁之前再来加锁,一样会加锁失败,因此是不可重入的。解决不可重入问题也很简单:加锁时判断记录中是否存在unique_mutex的记录,如果存在且holder_id和当前竞争者id相同,则加锁成功。这样就可以解决不可重入问题。
  锁释放时机 :设想如果一个竞争者获取锁时候,进程挂了,此时distributed_lock表中的这条记录就会一直存在,其他竞争者无法加锁。为了解决这个问题,每次加锁之前我们先判断已经存在的记录的创建时间和当前系统时间之间的差是否已经超过超时时间,如果已经超过则先删除这条记录,再插入新的记录。另外在解锁时,必须是锁的持有者来解锁,其他竞争者无法解锁。这点可以通过holder_id字段来判定。
  数据库单点问题 :单个数据库容易产生单点问题:如果数据库挂了,我们的锁服务就挂了。对于这个问题,可以考虑实现数据库的高可用方案,例如MySQL的MHA高可用解决方案。 基于缓存实现分布式锁,以Redis为例
  使用Jedis来和Redis通信。 加锁public class RedisTool {      private static final String LOCK_SUCCESS = "OK";     private static final String SET_IF_NOT_EXIST = "NX";     private static final String SET_WITH_EXPIRE_TIME = "PX";      /**      * 加锁      * @param jedis Redis客户端      * @param lockKey 锁的key      * @param requestId 竞争者id      * @param expireTime 锁超时时间,超时之后锁自动释放      * @return       */     public static boolean getDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {         String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);         return "OK".equals(result);     }  }
  可以看到,我们加锁就一行代码:
  jedis.set(String key, String value, String nxxx, String expx, int time);
  这个set()方法一共五个形参:
  第一个为key,我们使用key来当锁,因为key是唯一的。
  第二个为value,这里写的是锁竞争者的id,在解锁时,我们需要判断当前解锁的竞争者id是否为锁持有者。
  第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作。
  第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期时间的设置,具体时间由第五个参数决定;
  第五个参数为time,与第四个参数相呼应,代表key的过期时间。
  总的来说,执行上面的set()方法就只会导致两种结果:1.当前没有锁(key不存在),那么久进行加锁操作,并对锁设置一个有效期,同时value表示加锁的客户端。2.已经有锁存在,不做任何操作。
  上述解锁请求中, SET_IF_NOT_EXIST  (不存在则执行)保证了加锁请求的排他性,缓存超时机制保证了即使一个竞争者加锁之后挂了,也不会产生死锁问题:超时之后其他竞争者依然可以获取锁。通过设置value为竞争者的id,保证了只有锁的持有者才能来解锁,否则任何竞争者都能解锁,那岂不是乱套了。解锁public class RedisTool {      private static final Long RELEASE_SUCCESS = 1L;      /**      * 释放分布式锁      * @param jedis Redis客户端      * @param lockKey 锁      * @param requestId 锁持有者id      * @return 是否释放成功      */     public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {         String script = "if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end";         Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));         return RELEASE_SUCCESS.equals(result);     } }
  解锁的步骤: 判断当前解锁的竞争者id是否为锁的持有者,如果不是直接返回失败,如果是则进入第2步。 删除key,如果删除成功,返回解锁成功,否则解锁失败。
  注意到这里解锁其实是分为2个步骤,涉及到解锁操作的一个原子性操作问题。这也是为什么我们解锁的时候用Lua脚本来实现,因为Lua脚本可以保证操作的原子性。那么这里为什么需要保证这两个步骤的操作是原子操作呢?
  设想:假设当前锁的持有者是竞争者1,竞争者1来解锁,成功执行第1步,判断自己就是锁持有者,这是还未执行第2步。这是锁过期了,然后竞争者2对这个key进行了加锁。加锁完成后,竞争者1又来执行第2步,此时错误产生了:竞争者1解锁了不属于自己持有的锁。可能会有人问为什么竞争者1执行完第1步之后突然停止了呢?这个问题其实很好回答,例如竞争者1所在的JVM发生了GC停顿,导致竞争者1的线程停顿。这样的情况发生的概率很低,但是请记住即使只有万分之一的概率,在线上环境中完全可能发生。因此必须保证这两个步骤的操作是原子操作。 分析
  是否可重入 :以上实现的锁是不可重入的,如果需要实现可重入,在 SET_IF_NOT_EXIST  之后,再判断key对应的value是否为当前竞争者id,如果是返回加锁成功,否则失败。
  锁释放时机 :加锁时我们设置了key的超时,当超时后,如果还未解锁,则自动删除key达到解锁的目的。如果一个竞争者获取锁之后挂了,我们的锁服务最多也就在超时时间的这段时间之内不可用。
  Redis单点问题 :如果需要保证锁服务的高可用,可以对Redis做高可用方案:Redis集群+主从切换。目前都有比较成熟的解决方案。 基于Zookeeper实现分布式锁加锁和解锁流程
  利用Zookeeper创建临时有序节点来实现分布式锁: 当一个客户端来请求时,在锁的空间下面创建一个临时有序节点。 如果当前节点的序列是这个空间下面最小的,则代表加锁成功,否则加锁失败,加锁失败后设置Watcher,等待前面节点的通知。 当前节点监听其前面一个节点,如果前面一个节点删除了就通知当前节点。 当解锁时当前节点通知其后继节点,并删除当前节点。
  其基本思想类似于AQS中的等待队列,将请求排队处理。其流程图如下:
  分析
  解决不可重入 :客户端加锁时将主机和线程信息写入锁中,下一次再来加锁时直接和序列最小的节点对比,如果相同,则加锁成功,锁重入。
  锁释放时机 :由于我们创建的节点是顺序临时节点,当客户端获取锁成功之后突然session会话断开,ZK会自动删除这个临时节点。
  单点问题 :ZK是集群部署的,主要一半以上的机器存活,就可以保证服务可用性。 利用curator实现
  Zookeeper第三方客户端curator中已经实现了基于Zookeeper的分布式锁。利用curator加锁和解锁的代码如下: // 加锁,支持超时,可重入 public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {     try {         return interProcessMutex.acquire(timeout, unit);     } catch (Exception e) {         e.printStackTrace();     }     return true; } // 解锁 public boolean unlock() {     try {         interProcessMutex.release();     } catch (Throwable e) {         log.error(e.getMessage(), e);     } finally {         executorService.schedule(new Cleaner(client, path), delayTimeForClean, TimeUnit.MILLISECONDS);     }     return true; } 三种方案比较

四年六迭代,目前最好折叠屏?一文看懂OPPOFindN的前代今生作为此次OPPO未来科技大会2021的重磅炸弹,OPPOFindN的发布对整个折叠屏生态来说可谓着革新意义。在这当中不止于近乎无痕的屏幕观感,所开发的一系列便捷交互与功能创新也是此最低2000元起,这3款12G大内存手机价格真香,再用三五年也不落后虽然说现在8GB的手机内存已足够使用,但是手机内存越大运行会越流畅,同时随着手机价格的下调,很多12GB大内存手机价格也已经非常实惠,现在购买12GB大内存,手机还可以多用三五年也阿诗丹顿台式洗碗机人性化设计,尊重人的使用体验自阿诗丹顿台式洗碗机推出以来,便广受各地消费者欢迎,尤其是小家庭用户,阿诗丹顿台式洗碗机极少地占据厨房空间,以及使用步骤的简化,超高的性价比让经济条件不怎么宽裕的消费者也享受到了自LinuxShell教程想玩转linux就请一直看下去Shell教程Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。Shell是指一种应用程序,这个应用程序提供了一个爱奇艺涨价,为何大家那么难接受?编辑于斌出品潮起网于见专栏长久以来,每次一提到爱奇艺之困,所有人都知道其困在商业模式,而非它动辄就频繁遭遇的VVIP质疑会员涨价或变相涨价等等。可即便问题如此明晰,爱奇艺在公司经营Java常见的排序算法,一次跟你说明白中心思想通过无序区中的相邻的记录的比较和位置的交换,使记录较小的的数有如气泡一样逐渐往上漂浮直至浮出水面。代码实现publicintsort(intsourceArray)1。遍历无现金时代来临,欧美发达国家为何对移动支付不太感兴趣?十年前大家出门购物主要是以现金支付为主,一二线城市的少部分人会使用信用卡付费10年后移动支付横扫国内支付领域,除了偏远地区以及一些上了年纪的人,很少再有现金支付的情况了。移动支付在国内快递满意度公布低分的快递企业有2家,申通还不算是最差2021年12月,关于申通快递扔快递的视频在网上流传,引来外界广泛的讨论。就在前不久,国家邮政局组织第三方机构对2021年第三季度快递服务满意度进行了调查。报告指出,2021年第三华为P50折叠屏定价预测,受三星和OPPO的影响?华为P50Pocket折叠屏曝光的就挺突然的,虽然之前各种设计专利图也不少,但是这么快就要发布我们是没想到的,而且之前的名称都叫MateV是吧?但是人家居然是P50系列,所以现在的目前全球QQ等级最高的多少级了?需要多久才能达到QQ作为腾讯的起家产品,距今已有22年的发展时间了,相信不少人对QQ也是有着说不完的感情。那你还记得曾经我们最爱攀比的QQ等级吗?腾讯也是利用这一点,推出了不少会员加速服务来让用户内卷下的互联网跳槽当下很多人说内卷,现实一点讲,行业机会确实在减少。从招聘角度看,P8及以上的职位在大量减少,以前是机会找人,现在更多是人找机会P6P7的职位要求逐渐提高,甲方开始注重性价比。总而言
什么是密码学?计算机技术的进步使数据更易于访问,虽然这可能提供巨大的优势,但它也有缺点。在线数据面临许多威胁,包括盗窃和腐败。密码学(或密码学)是一种可以保护信息免受与数据存储和分发相关的风险的华为太子李一男,27岁当上副总裁身价10亿,45岁陷750万牢狱之灾华为作为科技领域的第一大民营企业,其实力不一般。但实际上,早在华为刚成立时,其营收情况并不是很好。而将华为发展起来的人中,被称为华为太子的李一男起着至关重要的作用。据了解,在他的带微商教父被查,下一个是谁?近日,深圳触电电子商务有限公司创始人,微商的代表性人物龚文祥的一则群公告刷屏网络,在群公告中,龚文祥说他被工商税务公安法院等专案组联合查处,公司已经破产,高额处罚个人已经到了负债累手机性价比之王是哪个?首选5酷派cool1C参考价格899元2017跑分最高的千元机以下是2017年最新安兔兔跑分排行榜前十名,上榜的千元机只有两台上榜的千元机包括乐视Pro3和小米5S,一起来了解一下怎样查华为荣耀手机生产日期?你的手机是哪一天被生产出来的呢?绝大部分的人,都不知道的一个问题?很多人都会玩手机,但是绝大部分人都不知道自己的手机,到底是哪一天生产的?今天就来给大家分享一个,平时我们该怎样去查差距一代的手机还值得入手吗?我认为还是隔代升级比较好,以高通芯片为例,855升级到骁龙865处理器提升了百分之十五左右,865升级到骁龙888也是差不多1520但是888发热降频的情况很多用户都有反馈,所以今服务普惠金融高质量发展来源人民网人民日报作为国内首家成立的互联网银行,微众银行的设立宗旨,就是希望通过特色化差异化,为个人客户和小微企业客户提供金融服务。自成立以来,我们始终坚持普惠金融的定位不变,坚持朝阳供电公司计量中心用信息化大数据护航电力精准计量本报讯(孙伟付玉)年初以来,国网朝阳供电公司计量中心深入挖掘技术创新新富矿,将信息化大数据融入精准计量,实现了电力计量服务链全面升级。智能化仓储效率高。为提高仓储工作效率,今年8月开发新药涉猎金融量子技术改变世界的四种方式来源科技日报开发新药涉猎金融应对气变量子技术改变世界的四种方式科技创新世界潮本报记者刘霞美国计算机巨头IBM近日宣布研制出一台能运行127个量子比特的量子计算机鹰,这是迄今全球最大纳微半导体上市背后第三代半导体潮起氮化镓功率市场年增率达7321世纪经济报道记者倪雨晴深圳报道2017年12月5日,从事半导体行业多年的查莹杰还在犹豫,是否要从大厂投身一家创业公司。这是一家专注于氮化镓的第三代半导体企业,名为纳微半导体,刚12月行情1500元的骁龙870,全价位段手机推荐欢迎收看12月3日更新的手机推荐栏目。经过双十一之后,很多手机都没有再回到以前的价格,像真我moto等品牌的很多机型,价格都比双十一便宜。在10月底到12月初发布的新机包括Redm