分布式锁三种实现方式及对比_Redis

来源:脚本之家  责任编辑:小易  

1.利用节点名称的唯一性来实现共享锁ZooKeeper抽象出来的节点结构是一个和unix文件系统类似的小型的树状的目录结构。ZooKeeper机制规定:同一个目录下只能有一个唯一的文件名。例如:我们在Zookeeper目录/test目录下创建,两个客户端创建一个名为Lock节点,只有一个能够成功。算法思路:利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁。解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。基于以上机制,利用节点名称唯一性机制的共享锁算法流程如图所示:该共享锁实现很符合我们通常多个线程去竞争锁的概念,利用节点名称唯一性的做法简明、可靠。由上述算法容易看出,由于客户端会同时收到/test/Lock被删除的通知,重新进入竞争创建节点,故存在"惊群现象。使用该方法进行测试锁的性能列表如下:总结 这种方案的正确性和可靠性是ZooKeeper机制保证的,实现简单。缺点是会产生“惊群”效应,假如许多客户端在等待一把锁,当锁释放时候所有客户端都被唤醒,仅仅有一个客户端得到锁。2.利用临时顺序节点实现共享锁的一般做法首先介绍一下,Zookeeper中有一种节点叫做顺序节点,故名思议,假如我们在/lock/目录下创建节3个点,ZooKeeper集群会按照提起创建的顺序来创建节点,节点分别为/lock/0000000001、/lock/0000000002、/lock/0000000003。ZooKeeper中还有一种名为临时节点的节点,临时节点由某个客户端创建,当客户端与ZooKeeper集群断开连接,则开节点自动被删除。利用上面这两个特性,我们来看下获取实现分布式锁的基本逻辑:客户端调用create()方法创建名为“locknode/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。客户端调用getChildren(“locknode”)方法来获取所有已经创建的子节点,同时在这个节点上注册上子节点变更通知的Watcher。客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。如果在步骤3中发现自己并非是所有子节点中最小的,说明自己还没有获取到锁,就开始等待,直到下次子节点变更通知的时候,再进行子节点的获取,判断是否获取锁。释放锁的过程相对比较简单,就是删除自己创建的那个子节点即可。上面这个分布式锁的实现中,大体能够满足了一般的分布式集群竞争锁的需求。这里说的一般性场景是指集群规模不大,一般在10台机器以内。不过,细想上面的实现逻辑,我们很容易会发现一个问题,步骤4,“即获取所有的子点,判断自己创建的节点是否已经是序号最小的节点”,这个过程,在整个分布式锁的竞争过程中,大量重复运行,并且绝大多数的运行结果都是判断出自己并非是序号最小的节点,从而继续等待下一次通知—这个显然看起来不怎么科学。客户端无端的接受到过多的和自己不相关的事件通知,这如果在集群规模大的时候,会对Server造成很大的性能影响,并且如果一旦同一时间有多个节点的客户端断开连接,这个时候,服务器就会像其余客户端发送大量的事件通知—这就是所谓的惊群效应。而这个问题的根源在于,没有找准客户端真正的关注点。我们再来回顾一下上面的分布式锁竞争过程,它的核心逻辑在于:判断自己是否是所有节点中序号最小的。于是,很容易可以联想的到的是,每个节点的创建者只需要关注比自己序号小的那个节点。3、利用临时顺序节点实现共享锁的改进实现下面是改进后的分布式锁实现,和之前的实现方式唯一不同之处在于,这里设计成每个锁竞争者,只需要关注”locknode”节点下序号比自己小的那个节点是否存在即可。算法思路:对于加锁操作,可以让所有客户端都去/lock目录下创建临时顺序节点,如果创建的客户端发现自身创建节点序列号是/lock/目录下最小的节点,则获得锁。否则,监视比自己创建节点的序列号小的节点(比自己创建的节点小的最大节点),进入等待。对于解锁操作,只需要将自身创建的节点删除即可。具体算法流程如下图所示:使用上述算法进行测试的的结果如下表所示:该算法只监控比自身创建节点序列号小(比自己小的最大的节点)的节点,在当前获得锁的节点释放锁的时候没有“惊群”。总结 利用临时顺序节点来实现分布式锁机制其实就是一种按照创建顺序排队的实现。这种方案效率高,避免了“惊群”效应,多个客户端共同等待锁,当锁释放时只有一个客户端会被唤醒。4、使用menagerie其实就是对方案3的一个封装,不用自己写代码了。直接拿来用就可以了。menagerie基于Zookeeper实现了java.util.concurrent包的一个分布式版本。这个封装是更大粒度上对各种分布式一致性使用场景的抽象。其中最基础和常用的是一个分布式锁的实现:org.menagerie.locks.ReentrantZkLock,通过ZooKeeper的全局有序的特性和EPHEMERAL_SEQUENTIAL类型znode的支持,实现了分布式锁。具体做法是:不同的client上每个试图获得锁的线程,都在相同的basepath下面创建一个EPHEMERAL_SEQUENTIAL的node。EPHEMERAL表示要创建的是临时znode,创建连接断开时会自动删除;SEQUENTIAL表示要自动在传入的path后面缀上一个自增的全局唯一后缀,作为最终的path。因此对不同的请求ZK会生成不同的后缀,并分别返回带了各自后缀的path给各个请求。因为ZK全局有序的特性,不管client请求怎样先后到达,在ZKServer端都会最终排好一个顺序,因此自增后缀最小的那个子节点,就对应第一个到达ZK的有效请求。然后client读取basepath下的所有子节点和ZK返回给自己的path进行比较,当发现自己创建的sequential node的后缀序号排在第一个时,就认为自己获得了锁;否则的话,就认为自己没有获得锁。这时肯定是有其他并发的并且是没有断开的client/线程先创建了nodewww.zgxue.com防采集请勿采集本网。

分布式锁三种实现方式:

1.利用节点名称的唯一性来实现共享锁 利用上面这两个特性,我们来看下获取实现分布式锁的基本逻辑: 客户端调用create()方法创建名为“locknode/guid-lock-”的节点,需要注意的是,这里节点的

1. 基于数据库实现分布式锁;

不需要加密货币也可以工作,在成熟的分布式一致性算法(Pasox、Raft)基础上,实现秒级共识验证。缺点 去中心化程度不如bitcoin,更适合多方参与的多中心商业模式。Paxos 这是一种传统的分布式一致性算法,

2. 基于缓存(Redis等)实现分布式锁;

一、zookeeper 1、实现原理: 利用该点即可很轻松地实现分布式锁。三、redis分布式锁 redis分布式锁即可以结合zk分布式锁锁高度安全和memcached并发场景下效率很好的优点,可以利用jedis客户端实现

3. 基于Zookeeper实现分布式锁;

现代CMM不仅能在计算机控制下完成各种复杂测量,而且可以通过与数控机床交换信息,实现对加工的控制,并且还可以根据测量数据,实现反求工程。目前,CMM已广泛用于机械制造业、汽车工业、电子工业、航空

一, 基于数据库实现分布式锁

时间并行性概念的实现方式就是采用流水处理部件。这是一种非常经济而实用的并行技术,能保证计算机系统具有较高的性能价格比。目前的高性能微型机几乎无一例外地使用了流水技术。资源共享也是在并行性概念

1. 悲观锁

利用select … where … for update 排他锁

注意: 其他附加功能与实现一基本一致,这里需要注意的是“where name=lock ”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。

2. 乐观锁

所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有update version失败后才能觉察到。我们的抢购、秒杀就是用了这种实现以防止超卖。

通过增加递增的版本号字段实现乐观锁

二, 基于缓存(Redis等)实现分布式锁

1. 使用命令介绍:

(1)SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

(2)expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

(3)delete

delete key:删除key

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

2. 实现思想:

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

三, 基于Zookeeper实现分布式锁

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock;

(2)线程A想获取锁就在mylock目录下创建临时顺序节点;

(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;

(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;

(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。

四,对比

数据库分布式锁实现

缺点:

1.db操作性能较差,并且有锁表的风险

2.非阻塞操作失败后,需要轮询,占用cpu资源;

3.长时间不commit或者长时间轮询,可能会占用较多连接资源

Redis(缓存)分布式锁实现

缺点:

1.锁删除失败 过期时间不好控制

2.非阻塞,操作失败后,需要轮询,占用cpu资源;

ZK分布式锁实现

缺点:性能不如redis实现,主要原因是写操作(获取锁释放锁)都需要在Leader上执行,然后同步到follower。

总之:ZooKeeper有较好的性能和可靠性。

从理解的难易程度角度(从低到高)数据库 > 缓存 > Zookeeper

从实现的复杂性角度(从低到高)Zookeeper >= 缓存 > 数据库

从性能角度(从高到低)缓存 > Zookeeper >= 数据库

从可靠性角度(从高到低)Zookeeper > 缓存 > 数据库

分布式锁的三种实现方式分别是:基于数据库实现分布式锁、基于缓存(Redis等)实现分布式锁、基于Zookeeper实现分布式锁。一、基于数据库实现分布式锁1、悲观锁利用select…where…for update 排他锁。注意:其他附加功能与实现一基本一致,这里需要注意的是“where name=lock”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。2、乐观锁所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有update version失败后才能觉察到,抢购和秒杀就是用了这种实现以防止超卖,通过增加递增的版本号字段实现乐观锁。二、基于缓存(Redis等)实现分布式锁1、使用命令介绍:(1)SETNXSETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。(2)expireexpire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。(3)deletedelete key:删除key在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。2、实现思想:(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。三、基于Zookeeper实现分布式锁ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:(1)创建一个目录mylock。(2)线程A想获取锁就在mylock目录下创建临时顺序节点。(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁。(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点。(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。扩展资料;一、数据库分布式锁实现的缺点:1、db操作性能较差,并且有锁表的风险。2、非阻塞操作失败后,需要轮询,占用cpu资源。3、长时间不commit或者长时间轮询,可能会占用较多连接资源。二、Redis(缓存)分布式锁实现的缺点:1、锁删除失败,过期时间不好控制。2、非阻塞,操作失败后,需要轮询,占用cpu资源。三、ZK分布式锁实现的缺点:性能不如redis实现,主要原因是写操作(获取锁释放锁)都需要在Leader上执行,然后同步到follower内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • springboot使用redisson实现分布式锁(秒杀系统)
  • java redis分布式锁的正确实现方式详解
  • 谈谈redis分布式锁的正确实现方法
  • redis实现分布式锁和等待序列的方法示例
  • 浅谈redis分布式锁的正确实现方式
  • 浅谈java(springboot)基于zookeeper的分布式锁实现
  • java使用redisson分布式锁实现原理
  • java基于jedislock—redis分布式锁实现示例代码
  • redis实现分布式锁的几种方法总结
  • redis教程之代理ip池设计方法详解
  • 如何使用redis保存用户会话session详解
  • 内存型数据库redis持久化小结
  • redis安装与使用方法小结
  • linux安装配置及使用redis
  • redisdesktopmanager无法远程连接redis的完美解决方法
  • 使用redis实现ua池的方案
  • redis教程(十五):c语言连接操作代码实例
  • redis配置文件redis.conf详细配置说明
  • redis sentinel服务配置流程(详解)
  • 分布式锁的三种实现方式
  • 如何用Redlock实现分布式锁
  • 如何用Redlock实现分布式锁
  • java分布式锁的实现方式有哪些
  • 如何实现一个zookeeper的分布式锁
  • 分布式共识包含哪三种方法?
  • java trylock能实现分布式锁吗
  • 三坐标测量仪初步知识
  • 并行处理技术的三种形式
  • 什么是分布式编程
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全mssqlmysqlmariadboracledb2mssql2008mssql2005sqlitepostgresqlmongodbredisaccess数据库文摘数据库其它首页redisspringboot使用redisson实现分布式锁(秒杀系统)java redis分布式锁的正确实现方式详解谈谈redis分布式锁的正确实现方法redis实现分布式锁和等待序列的方法示例浅谈redis分布式锁的正确实现方式浅谈java(springboot)基于zookeeper的分布式锁实现java使用redisson分布式锁实现原理java基于jedislock—redis分布式锁实现示例代码redis实现分布式锁的几种方法总结redis教程之代理ip池设计方法详解如何使用redis保存用户会话session详解内存型数据库redis持久化小结redis安装与使用方法小结linux安装配置及使用redisredisdesktopmanager无法远程连接redis的完美解决方法使用redis实现ua池的方案redis教程(十五):c语言连接操作代码实例redis配置文件redis.conf详细配置说明redis sentinel服务配置流程(详解)超强、超详细redis数据库入门教程redis常用命令、常见错误、配置技redis操作命令总结redis中5种数据结构的使用场景介64位windows下安装redis教程redis中使用redis-dump导出、导入redis中统计各种数据大小的方法redis常用命令小结让redis在你的系统中发挥更大作用centos 6.6下redis安装配置记录详谈redis优化配置和redis.conf说明(推荐linux下redis安装配置教程详解redis数据结构之压缩列表超强、超详细redis数据库入门教程springsession+redis实现集群会话共享的方深入了解redis的性能linux 下redis5.0.0安装教程详解基于redis分布式锁实现秒杀功能redis分布式锁的正确实现方法总结一篇文章揭秘redis的磁盘持久化机制
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved