分布式锁实现方案
后端架构803 字预计 2 分钟阅读
详细剖析分布式环境下数据一致性锁策略,重点解密 Redis Redlock 与 ZooKeeper 的实现对比。
引言
在单机环境下,我们可以直接使用语言层面的互斥锁(如 Java 的 ReentrantLock 或 Go 的 sync.Mutex)来保障并发安全。但在分布式集群部署的场景下,这些锁机制便失去了效用。我们需要一种在全局多节点间共享的协调器,即分布式锁。
基于 Redis 的分布式锁
SETNX 的正确姿势
最基础的 Redis 分布式锁可以通过以下命令原子地创建:
SET resource_key unique_value NX PX 30000
NX:表示只有 key 不存在时才设置成功,保证了互斥性。PX 30000:设置 30 秒的过期时间,防止持有锁的客户端挂掉后导致死锁。unique_value:每次获取锁时必须生成全局唯一的随机字符串,在释放锁时,必须使用 Lua 脚本校验该值是否与持有者一致,防止误删其他客户端的锁。
锁续期机制 (Watchdog)
如果业务执行时间超出了锁的过期时间,锁会被提前释放,导致并发安全问题。为此,可以通过在后台启动一个守护线程(在 Redisson 中称为看门狗 Watchdog),每隔一段时间(如 10 秒)自动延长锁的生命周期,直至业务执行完毕显式释放锁。
Redlock(红锁)的争议与局限
在 Redis 哨兵或集群架构下,如果主节点在同步锁状态到从节点前挂掉了,会导致多客户端同时拿到锁。为了解决该问题,Redis 作者提出了 Redlock 算法:客户端尝试向 N 个(N 必须为奇数,如 5 个)完全独立的 Redis 节点写入锁,只有当超过半数(N/2 + 1)的节点成功写入时,才算真正获取到锁。然而,该算法极其依赖系统时钟,且在高网络延迟下依然存在漏洞,实际工程中需谨慎评估。
基于 ZooKeeper 的分布式锁
临时顺序节点原理
ZooKeeper 通过其树状目录结构和临时顺序节点(Ephemeral Sequential Nodes)完美地实现了排队锁机制:
- 客户端在指定路径下创建临时顺序节点。
- 客户端获取该路径下的所有子节点,并判断自己创建的节点是不是序号最小的那个。
- 如果是最小的,则成功获得锁。
- 如果不是,则对前一个节点进行
Watch监听,进入等待状态。
为什么 ZK 锁比 Redis 锁更安全
- 无过期时间风险:ZK 的临时节点与客户端的会话绑定。如果客户端挂了,TCP 连接断开,会话失效后 ZK 会自动删除该节点,无需担心死锁,也免去了锁续期的烦恼。
- 强一致性:ZK 采用 CP 架构(CP in CAP),保证了数据的强一致性,不会出现因为主从异步同步延迟导致两个客户端同时拿到锁的极端情况。
- 开销问题:ZK 每次加锁和释放锁都需要进行频繁的节点创建与删除,且需要进行共识同步,吞吐量要明显低于基于内存读取的 Redis 锁。