Redis的分布式锁
主要使用setNx()设置值
Redis set key 时的一个 NX 参数可以保证在这个 key 不存在的情况下写入成功,并且再加上 EX 参数可以让该 key 在超时之后自动删除。
解锁
如果进程A获取了锁设置了超时时间,但是由于执行周期较长导致超时时间之后锁就自动释放了,这时进程B获取了该锁执行很快就释放锁。就会导致进程B将进程A的锁释放掉了。为了避免这种情况,需要每次解锁的时候判断锁是否是自己的,可以当获取锁的时候,将进程id作为value进行传递。
setNx()有两步操作,设置值,设置过期时间,这个会导致未设置过期时间锁变成永久性锁
可以使用lua脚本保证加锁的操作属于原子性操作
Redis的版本2.6.2之后setNx (key,value,time,NX)保证原子性
Setnx(key,value),get(),setget()
ZK的分布式锁
ZK锁主要利用ZNode的唯一性做锁
最普通的锁:ZooKeeper机制规定同一个目录下只能有一个唯一的文件名,zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建/lock/${lock_name}_lock节点,最终成功创建的那个客户端也即拥有了这把锁,创建失败的可以选择监听继续等待,还是放弃抛出异常实现独占锁。
利用临时顺序节点做锁
/lock已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。
算法思路:对于加锁操作,可以让所有客户端都去/lock目录下创建临时顺序节点,如果创建的客户端发现自身创建节点序列号是/lock/目录下最小的节点,则获得锁。否则,监视比自己创建节点的序列号小的节点(比自己创建的节点小的最大节点),进入等待。
对于解锁操作,只需要将自身创建的节点删除即可。
优缺点:
优点:有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
缺点:性能上可能并没有缓存服务那么高,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能。ZK 中创建和删除节点只能通过 Leader 服务器来执行,然后将数据同步到所有的 Follower 机器上。还需要对 ZK的原理有所了解。
Zookeeper的结构
表象是文件夹类型,树结构,这个存储结构是一个树形结构,其上的每一个节点,我们称之为“znode”。如下如所示
Ø 每一个znode默认能够存储1MB的数据(对于记录状态性质的数据来说,够了)
Ø 可以使用zkCli命令,登录到zookeeper上,并通过ls、create、delete、sync等命令操作这些znode节点
Ø znode除了名称、数据以外,还有一套属性:zxid。这套zid与时间戳对应,记录zid不同的状态(后续我们将用到)
那么每个znode结构又是什么样的呢?如下图所示
ZNode中的存在类型
我们知道了zookeeper内部维护了一套数据结构:由znode构成的集合,znode的集合又是一个树形结构。每一个znode又有很多属性进行描述。并且znode的存在性还分为四类,如下如所示:
znode是由客户端创建的,它和创建它的客户端的内在联系,决定了它的存在性:
Ø PERSISTENT-持久化节点:创建这个节点的客户端在与zookeeper服务的连接断开后,这个节点也不会被删除(除非您使用API强制删除)。
Ø PERSISTENT_SEQUENTIAL-持久化顺序编号节点:当客户端请求创建这个节点A后,zookeeper会根据parent-znode的zxid状态,为这个A节点编写一个全目录唯一的编号(这个编号只会一直增长)。当客户端与zookeeper服务的连接断开后,这个节点也不会被删除。
Ø EPHEMERAL-临时目录节点:创建这个节点的客户端在与zookeeper服务的连接断开后,这个节点(还有涉及到的子节点)就会被删除。
Ø EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点:当客户端请求创建这个节点A后,zookeeper会根据parent-znode的zxid状态,为这个A节点编写一个全目录唯一的编号(这个编号只会一直增长)。当创建这个节点的客户端与zookeeper服务的连接断开后,这个节点被删除。
另外,无论是EPHEMERAL还是EPHEMERAL_SEQUENTIAL节点类型,在zookeeper的client异常终止后,节点也会被删除。
博客地址:https://blog.csdn.net/wufaliang003/article/details/76043892