分布式锁的两种实现方式(基于redis和基于zookeeper)

先来讲说什么是分布式锁,简单来讲,分布式锁就是在分布式并发场景中,可以实现多节点的代码同步的一种机制。从实现角度来看,主要有两种方式:基于redis的方式和基于zookeeper的方式,下面分别简单介绍下这两种方式:html

请尊重做者劳动成果,转载请标明原文连接:node

http://www.javashuo.com/article/p-rjgwjyjv-db.htmlweb

1、基于redis的分布式锁实现redis

1.获取锁 数据库

redis是一种key-value形式的NOSQL数据库,经常使用于做服务器的缓存。从redis v2.6.12开始,set命令开始变成以下格式:缓存

SET key value [EX seconds] [PX milliseconds] [NX|XX]

除key和value外,EX是超时时间,NX表示只有在key不存在的时候才会设置key的值,而XX表示在key存在的时间才会设置key的值。NX机制就是基于redis分布式锁的核心。可以解决如下问题:服务器

1)节点1获取key,而且设置超时时间后,还没来得及释放就挂掉了——这里EX超时时间会发挥做用,超时后自动释放锁。并发

2)刚获取到锁,还没来得及设置超时时间就挂了——这里设置key和设置超时时间是原子操做,若是出现这种状况,会返回0,即获取不到锁。分布式

2.释放锁lua

为了解决非原子操做带来的问题,常采用lua脚本实现。lua脚本的操做会被认为是原子性的,相似于事务。伪代码以下:

String luaScript =

"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

redisClient.eval(luaScript , Collections.singletonList(key), Collections.singletonList(threadId)); 

2、基于zookeeper的分布式锁实现 

zookeeper是一种分布式协调服务,其中每一个节点称为znode,并有本身独立的路径。 znode有四种类型:

  持久节点:默认的节点类型。建立节点的客户端与zookeeper断开链接后,该节点依旧存在 。
  持久节点顺序节点:所谓顺序节点,就是在建立节点时,Zookeeper根据建立的时间顺序给该节点名称进行编号:
  临时节点:和持久节点相反,当建立节点的客户端与zookeeper断开链接后,临时节点会被删除:
  临时顺序节点:结合和临时节点和顺序节点的特色:在建立节点时,Zookeeper根据建立的时间顺序给该节点名称进行编号;当建立节点的客户端与zookeeper断开链接后,临时节点会被删除。

下面看看是怎样基于上面的四类节点实现分布式锁的。

1.获取锁

1)在Zookeeper当中建立一个持久节,当第一个客户端Client1想要得到锁时,须要在这个节点下面建立一个临时顺序节点。
2)Client1查找持久节点下面全部的临时顺序节点并排序,判断本身所建立的节点是否是顺序最靠前的一个。若是是第一个节点,则成功得到锁。
3)若是再有一个客户端 Client2 前来获取锁,则在持久节点下面再建立一个临时顺序节点Lock2。
4)Client2查找持久节点下面全部的临时顺序节点并排序,判断本身所建立的节点Lock2是否是顺序最靠前的一个,结果发现节点Lock2并非最小的。
因而,Client2向排序仅比它靠前的节点Lock1注册Watcher,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态。
5)若是又有一个客户端Client3前来获取锁,则在持久节点下载再建立一个临时顺序节点Lock3。
Client3查找持久节点下面全部的临时顺序节点并排序,判断本身所建立的节点Lock3是否是顺序最靠前的一个,结果一样发现节点Lock3并非最小的。
因而,Client3向排序仅比它靠前的节点Lock2注册Watcher,用于监听Lock2节点是否存在。这意味着Client3一样抢锁失败,进入了等待状态。

2.释放锁 

释放锁就比较简单了,由于前面建立的临时顺序节点,因此在出现下面两种状况时,都会自动释放锁:

1)任务完成后,Client会释放锁。

2)任务没完成,Client就崩溃了,也会自动释放锁。 

相关文章
相关标签/搜索