java技术--分布概念之分布式锁(03)

1.目前主流的分布式锁实现方案有两种:

(1)基于redis:有开源redisson的jar包供使用
(2)基于zookeeper:有开源的curator的jar包供使用
(3)redis和zookeeper作分布式锁的区别:
   <1>zookeeper可靠性比redis强太多,只是效率低了点
   <2>如果并发量不是特别大,追求可靠性,首选zookeeper
   <3>为了效率,则首选redis实现

2.为什么使用分布式锁

(1)使用分布式锁的目的
   <1>保证同一时间只有一个客户端可以对共享资源进行操作 
(2)根据锁的用途还可以细分为以下两类
   <1>允许多个客户端操作共享资源
     1.1.这种情况下,对共享资源的操作一定是幂等性操作
     1.2.无论操作多少次都不会出现不同结果   
     1.3.在这里使用锁,就是为了避免重复操作共享资源从而提高效率
   <2>只允许一个客户端操作共享资源
     2.1.这种情况下,对共享资源的操作一般是非幂等性操作
     2.2.如果出现多个客户端操作共享资源,就可能意味着数据不一致,数据丢失  

3.单机模式下的分布式锁比较

(1)Redis
   <1>使用下面的命令加锁:SET resource_name my_random_value NX PX 30000
     1.1.my_random_value是由客户端生成的一个随机字符串,相当于是客户端持有锁的标志
     1.2.NX表示只有当resource_name对应的key值不存在的时候才能SET成功,相当于只有第一个请求的客户端才能获得锁
     1.3.PX 30000表示这个锁有一个30秒的自动过期时间
   <2>解锁:
     2.1.为了防止客户端1获得的锁,被客户端2给释放,采用下面的Lua脚本来释放锁
        if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1]) else return 0 end
     2.2.KEYS[1]的值为resource_name,ARGV[1]的值为my_random_value
     2.3.原理就是先获取锁对应的value值,保证和客户端穿进去的my_random_value值相等
     2.4.这样就能避免自己的锁被其他人释放,采取Lua脚本操作保证了原子性
(2)Zookeeper
   <1>zookeeper的分布式锁原理是利用了临时节点(EPHEMERAL)的特性
     1.1.当znode被声明为EPHEMERAL的后,如果创建znode的那个客户端崩溃了,那么相应的znode会被自动删除
     1.2.这样就避免了设置过期时间的问题
     1.3.客户端尝试创建一个znode节点,比如/lock
       1.3.1.第一个客户端就创建成功了,相当于拿到了锁
       1.3.2.而其它的客户端会创建失败(znode已存在),获取锁失败    

4.集群模式比较

(1)Redis
   <1>为了redis的高可用,一般都会给redis的节点挂一个slave,然后采用哨兵模式进行主备切换
    1.1.但由于Redis的主从复制(replication)是异步的,这可能会出现在数据同步过程中,master宕机
    1.2.slave来不及同步数据就被选为master,从而数据丢失
   <2>导致数据丢失具体流程如下所示:
    2.1.客户端1从Master获取了锁   
    2.2.Master宕机了,存储锁的key还没有来得及同步到Slave上,Slave升级为Master
    2.3.客户端2从新的Master获取到了对应同一个资源的锁
   <3>为了应对因Master宕机造成的数据丢失问题,提出了RedLock算法,步骤如下:
    3.1.假设有5个master节点,获取当前时间(单位毫秒)
    3.2.客户端轮流用相同的key和随机值在5个节点上请求锁
    3.3.请求锁时,会有一个和总的释放锁的时间相比小很多的超时时间
    3.4.可以防止一个客户端在某个宕掉宕master节点上阻塞过长时间
    3.5.在获取锁上所花费的时间,只有当客户端在大多数master节点上成功获取到锁
    3.6.并且总消耗时间不超过锁释放时间,这个锁就认为是获取成功了
    3.7.如果获取锁失败,客户端都会到每个master节点上释放锁
   <4>RedLock不足:节点崩溃重启后引发锁失效,针对同一资源会出现多个客户端持有锁   
    4.1.解决方案:采用延迟重启
    4.2.一个节点崩溃后,先不立即重启,而是等待一段时间在重启
    4.3.等待的时间大于锁的有效时间
   <5>时间跳跃问题:引发的锁失效问题
    5.1.解决方案:禁止认为修改系统时间,使用一个不会进行“跳跃”式调整系统时钟的ntpd程序
    5.2.这是通过认为补偿措施,降低不一致发生的概率
   <6>超时导致锁失效问题
    6.1.RedLock算法并没有解决操作共享资源超时时,导致锁失效的问题  
(2)Zookeeper
   <1>zookeeper在集群部署中,节点数量一般是奇数,且一定大于3
   <2>zookeeper的写数据原理:
     2.1.在client向follwer发出一个写的请求
     2.2.follwer把请求发送给leader
     2.3.leader接收到以后发起投票并通知follwer进行投票
     2.4.follwer把投票结果发送给leader,只要半数以上返回ACK信息,就认为通过
     2.5.leader将结果汇总后如果需要写入,开始写入同时把写入操作通知给leader,然后commit
     2.6.follwer把求情结果返回给client,zookeeper采用的是全局串行化操作
  <3>zookeeper做分布式锁的优点
     3.1.采用zookeeper作为分布式锁,要么获取不到锁,一旦获取,节点数据一定是一致的
     3.2.不会出现redis那种异步同步导致数据丢失的问题
     3.3.不存在时间跳跃问题:因为不依赖全局时间
     3.4.不存在超时导致锁失效问题:不依赖有效时间
  <4>缺点:效率没有redis作为分布式锁的效率高   

5.锁的其他特性比较

(1)redis的读写性能比zookeeper强大,高并发场景中,使用zookeeper会出现获取锁失败的情况
(2)zookeeper可以实现读写锁,redis不可以
(3)zookeeper的watch机制:
   <1>客户端试图创建zonde的时侯发现它已经不存在了,这时候创建失败
   <2>进入一种等待状态,当znode节点被删除的时候,zookeeper通过watch机制通知他
   <3>这样它就可以继续完才创建操作获取锁
     3.1.这可以让分布式锁在客户端用起来就像一个本地的锁一样
     3.2.加锁失败就阻塞,直到获取到锁为止
     3.3.这套机制,redis无法实现
(4)三种方案的比较
   <1>从理解的难易程度角度(从低到高): 数据库 > 缓存 > Zookeeper
   <2>从实现的复杂性角度(从低到高): Zookeeper >= 缓存 > 数据库
   <3>从性能角度(从高到低): 缓存 > Zookeeper >= 数据库
   <4>从可靠性角度(从高到低): Zookeeper > 缓存 > 数据库     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值