type
status
date
slug
summary
tags
category
icon
password
1、基于缓存的分布式锁
1.1、Redis的setNx
SET if Not exits
SETNX命令的原子性可以保证在多个客户端并发访问时,只有一个客户端能成功设置键值,并返回1。返回1的客户端就可以认为获取到了锁。
1.2、RedLock
Redlock算法是Redis官方提出的一种分布式锁算法,相比SETNX命令,它在安全性和性能方面都有一定的改进。
RedLock算法是Redis官方推荐的分布式锁实现方案,RedLock算法推荐至少使用5个Redis节点,这是为了确保锁的安全性和稳定性。这个设计主要基于以下两个原因:
- 避免单点故障:如果你只有一个Redis节点作为锁的存储,那么一旦这个节点出现故障(比如网络故障、硬件故障等),那么锁就会失效,导致应用程序无法正常工作。有多个节点可以降低这种风险。
- 保证一致性:RedLock算法要求你必须在大多数节点上成功设置锁,才算真正获取到锁。这是为了避免因为部分节点出现故障或网络延迟,导致其他客户端误认为锁已经被释放。只有当大多数节点都设置了锁,才能确保即使有部分节点出现问题,锁的状态仍然是正确的。选择5个节点,是为了保证即使有2个节点同时出现问题,其他3个节点的多数也能保证锁的正确性。
实际上,你可以根据自己的实际需求和环境,选择更多的节点。只要节点数是奇数,能够满足"大多数"的要求,就可以实现RedLock的设计目标。但是过多的节点可能会增加网络通信的延迟和复杂性,所以需要做出适当的权衡。
具体的实现步骤如下:
- 获取当前时间(单位毫秒)。
- 使用相同的key和随机value,依次尝试在所有的Redis节点上设定一个带有过期时间的key(通常使用setnx或set命令配合EX和NX参数)。
- 如果在大多数(比如说5个节点中的3个)Redis节点上设定成功,并且设定key的总耗时小于key的过期时间,则认为获取锁成功。
- 如果获取锁成功,那么锁的真正有效时间应该等于key的过期时间减去获取锁的总耗时。
- 如果获取锁失败,那么应该在所有的Redis节点上删除这个key。
java示例 用法如下 :
在这个示例中,首先创建了一个
RedissonClient
实例,并指定了Redis集群的地址。然后,通过RedissonClient
的getLock
方法获取了一个RLock
对象,该对象代表了一个可重入的分布式锁。调用lock
方法获取锁,执行业务逻辑,最后调用unlock
方法释放锁。不论业务逻辑是否执行成功,unlock
方法都会在finally
块中被执行,能避免因为异常导致的锁无法释放的情况。目前没有发现其他java Redis client有实现RedLock算法2、基于Zookeeper的分布式锁
Zookeeper是一个开源的分布式服务协调框架,它内部实现了一套Paxos一致性协议,可以提供基于Znode的分布式锁。当一个客户端想要获取锁时,可以在Zookeeper中创建一个临时顺序Znode,然后获取当前Znode列表,查看自己创建的Znode是否为最小的Znode,如果是则获得锁,如果不是,则监听比自己小的那个Znode的删除事件,当接收到删除事件时,再次进行获取锁的操作。
利用Curator创建临时顺序ZNode示例:
3、基于etcd的分布式锁
etcd是一个高可用的键值存储系统,用于共享配置和服务发现,它提供了一种基于租约的锁机制,客户端可以通过创建一个带租约的键值对来获取锁,当租约到期时,锁自动释放。
使用Jetcd实现分布式锁的基本步骤:
- 创建一个租约,并设置一个适当的TTL。这个租约将用于实现锁的自动过期。
- 尝试创建一个带有这个租约的键值对。这个键值对将代表锁。如果创建成功,那么就获取到了锁。
- 执行你的业务逻辑。
- 完成业务逻辑后,删除这个键值对,以释放锁。
代码示例:
- 作者:奥利弗
- 链接:https://www.aolifu.org/article/distributed-lock
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。