分布式锁原理有哪些,如何搞懂各种分布式锁?
摘要:分布式锁是控制分布式系统中不同进程节点对共享资源进行互斥访问的核心手段,其核心目标是保证在分布式环境下,同一时刻只有一个节点能获取到锁并执行临界区代码,避免数据不一致、并发冲突等问题。 本文将从分布式锁的核心要求出发,逐一拆解Redis、
分布式锁是控制分布式系统中不同进程/节点对共享资源进行互斥访问的核心手段,其核心目标是保证在分布式环境下,同一时刻只有一个节点能获取到锁并执行临界区代码,避免数据不一致、并发冲突等问题。
本文将从分布式锁的核心要求出发,逐一拆解Redis、ZooKeeper、Etcd、数据库等主流分布式锁的实现原理、优缺点及适用场景,帮你全面掌握其核心逻辑。
一、分布式锁的核心要求
要实现一个可靠的分布式锁,必须满足以下4个核心条件,缺一不可:
互斥性:同一时刻,只有一个客户端能持有锁。
防死锁:锁必须有超时自动释放机制,避免客户端宕机后锁永久持有。
可重入性(可选):同一客户端获取锁后,可重复获取同一把锁,避免死锁。
高可用/高性能:锁的获取/释放过程要高效,且能应对节点宕机等故障。
此外,还需满足原子性(获取/释放锁的操作是原子的)、容错性(集群环境下不丢锁)等附加要求。
二、基于Redis的分布式锁
Redis是分布式锁最常用的实现方案,基于其单线程执行和丰富的数据结构,实现简单且性能高。
1. 基础版Redis分布式锁(SETNX + EXPIRE)
实现原理
利用Redis的SETNX(SET if Not eXists,不存在则设置)命令实现锁的获取,EXPIRE设置锁超时时间防死锁:
获取锁:执行SETNX lock_key client_id,若返回1则获取锁成功;返回0则锁已被持有,获取失败。
释放锁:执行DEL lock_key,删除锁键即释放。
防死锁:通过EXPIRE lock_key 10设置锁超时(如10秒),即使客户端宕机,锁也会自动释放。
核心缺陷
原子性问题:SETNX和EXPIRE是两个独立命令,若执行完SETNX后客户端宕机,EXPIRE未执行,锁会永久持有。
锁误释放:客户端A获取锁后超时,锁自动释放;客户端B获取锁,此时A恢复并执行DEL删除B的锁,导致锁被误删。
不可重入:同一客户端重复执行SETNX会返回0,无法获取锁。
2. 进阶版Redis分布式锁(SET命令原子化)
实现原理
Redis 2.6.12+版本支持SET命令的扩展参数,可将获取锁+设置超时合并为一个原子操作,解决基础版的原子性问题:
获取锁:执行SET lock_key client_id EX 10 NX,其中:
EX 10:设置超时时间10秒;
NX:仅当键不存在时设置,等价于SETNX。
释放锁:需先判断锁的持有者(client_id)是否为当前客户端,再执行DEL,避免误释放。
示例:GET lock_key获取值,若等于当前client_id则DEL。
解决的问题
解决了SETNX+EXPIRE的原子性问题,避免锁永久持有。
增加client_id校验,避免误释放其他客户端的锁。
3. 高级版Redis分布式锁(Redlock算法)
实现原理
针对Redis单节点故障导致的锁丢失问题,Redis官方提出Redlock算法,基于Redis集群(多主节点)实现高可用分布式锁:
客户端获取当前时间戳(毫秒)。
依次向N个独立的Redis主节点(通常N≥5)执行获取锁操作(同进阶版SET lock_key client_id EX 10 NX),每个节点设置超时时间(远小于锁总超时,如50ms)。
客户端统计成功获取锁的节点数,若成功数≥N/2+1且总耗时<锁超时时间,则认为获取锁成功;否则释放所有节点的锁。
释放锁:向所有Redis节点执行释放锁操作(删除键)。
核心优势
解决单节点Redis宕机导致的锁丢失问题,高可用更强。
缺陷
性能损耗:需操作多个Redis节点,开销高于单节点锁。
复杂度高:算法实现繁琐,需处理节点宕机、时钟偏移等问题。
Redis分布式锁总结
方案
优点
缺点
适用场景
基础版(SETNX+EXPIRE)
实现简单
原子性差、易误释放、不可重入
低并发、简单场景
进阶版(SET EX NX)
原子性好、防误释放
单节点故障丢锁、不可重入
高并发、单Redis节点场景
Redlock算法
高可用、防丢锁
性能低、实现复杂
强一致性、高可靠场景
三、基于ZooKeeper的分布式锁
ZooKeeper是分布式协调框架,基于临时有序节点和Watcher监听机制实现分布式锁,天然支持高可用和防死锁。
1. 实现原理
ZooKeeper的数据模型是树形节点(ZNode),核心利用两类节点特性:
临时节点(EPHEMERAL):客户端宕机后,临时节点会自动删除,避免死锁。
有序节点(SEQUENTIAL):创建节点时自动分配递增序号,保证顺序。
