面试题答案
一键面试Redis分布式锁实现原理
单节点实现
- 加锁:客户端使用
SETNX
(SET if Not eXists)命令尝试设置一个键值对,键为锁的名称,值可以是一个唯一标识符(如客户端生成的UUID)。如果SETNX
命令返回1,表示设置成功,即获取到了锁;如果返回0,表示锁已被其他客户端持有。 - 解锁:客户端使用
DEL
命令删除锁对应的键值对来释放锁。但在删除锁时需要确保操作的原子性,防止误删其他客户端的锁。可以通过Lua脚本来实现,先判断锁的键值对是否与自己设置的一致,一致则删除。 - 防止死锁:通常会给锁设置一个过期时间,使用
EXPIRE
命令,这样即使持有锁的客户端出现异常未能主动释放锁,锁也会在过期时间后自动释放。
集群实现
- 加锁:在Redis集群模式下,基于Redlock算法。客户端需要向集群中的多个(N个)主节点发送
SETNX
命令尝试获取锁。当客户端从大多数(超过 N/2 + 1)的主节点成功获取到锁时,认为获取锁成功。 - 解锁:与单节点类似,客户端需要向所有获取锁的主节点发送
DEL
命令释放锁。同样要注意使用Lua脚本保证操作原子性。 - 防止死锁:同样为每个锁设置过期时间,避免死锁情况。但由于集群环境下可能存在网络分区等问题,过期时间设置需要更加谨慎。
Zookeeper分布式锁实现原理
- 加锁:客户端在Zookeeper的特定节点(如
/locks
)下创建一个临时顺序节点。Zookeeper会为每个新创建的节点分配一个唯一的顺序编号。客户端获取/locks
节点下的所有子节点,并判断自己创建的节点编号是否是最小的。如果是最小的,则获取到了锁;否则,需要对编号比自己小的最后一个节点注册Watcher
监听。 - 解锁:当客户端完成任务后,删除自己创建的临时顺序节点。Zookeeper会自动通知等待在该节点上的其他客户端(通过
Watcher
),这些客户端重新获取/locks
节点下的所有子节点,并再次判断自己是否可以获取锁。 - 防止死锁:由于是临时节点,当持有锁的客户端与Zookeeper的会话失效(如客户端崩溃或网络故障)时,Zookeeper会自动删除该客户端创建的临时顺序节点,从而释放锁,避免死锁。
二者主要不同点
- 实现方式:Redis基于简单的键值对操作,通过
SETNX
和DEL
等命令实现分布式锁;Zookeeper基于节点特性,通过创建临时顺序节点和监听机制实现分布式锁。 - 可靠性:Zookeeper采用的是CP(一致性和分区容错性)模型,在网络分区的情况下能够保证数据的一致性,所以分布式锁的可靠性较高;Redis在单节点模式下可靠性一般,在集群模式下基于Redlock算法,由于网络分区等因素,可能存在获取锁的不一致情况,更倾向于AP(可用性和分区容错性)模型。
- 性能:Redis单节点模式下性能较高,因为操作简单;但在集群模式下,由于需要与多个节点交互,性能会有所下降。Zookeeper由于需要创建节点、监听节点变化等操作,性能相对Redis单节点模式较低。
- 异常处理:Redis通过设置过期时间处理客户端异常导致的死锁;Zookeeper利用临时节点特性,客户端会话失效时自动释放锁,处理方式更加优雅。