Redis RedLock 的潜在失败场景
Redis RedLock 的设计目标是在分布式系统中提供一种相对可靠的锁机制,但它并不保证绝对的一致性,这主要源于 Redis 的几个特性:
- 网络分区和节点故障:在 Redis RedLock 的实现中,如果客户端成功地在多个 Redis 节点上设置了锁,但随后一些节点(如示例中的 C)在锁信息被持久化之前失败或重启,这可能导致锁的状态不一致。新的节点(如 F)加入后,如果它不了解之前锁的状态,就可能允许新的客户端(如客户端 2)获取到锁,尽管该锁可能仍被其他客户端持有。
- 时钟偏移:Redis RedLock 依赖于时间戳来判断锁是否过期,如果系统间存在时钟偏移,可能导致锁提前释放或延迟释放。
- 持久化问题:Redis 支持多种持久化策略(如 RDB 快照和 AOF 日志),但这些策略并不保证每次写操作都立即持久化到磁盘,因此节点故障可能导致最近的数据丢失。
Zookeeper 的锁一致性保障
相比之下,Zookeeper 通过其 ZAB(ZooKeeper Atomic Broadcast)协议提供了更强的一致性保障:
- 全局顺序:ZAB 协议保证了所有写操作的全局顺序性,这意味着在 Zookeeper 中创建或删除节点的操作会按照全局顺序执行,从而避免了锁状态的不一致。
- 持久性:Zookeeper 的数据存储在磁盘上,并且每个写操作都会同步到磁盘,确保了数据的持久性。即使 Zookeeper 节点崩溃,重启后也能从磁盘恢复最新的数据状态。
- 临时有序节点:在 Zookeeper 中,锁通常通过创建临时有序节点来实现。这些节点在客户端会话结束时会自动删除。如果客户端崩溃,其会话将超时,Zookeeper 会自动删除该客户端创建的临时节点,从而释放锁。此时,其他客户端可以安全地获取锁。
Zookeeper 的崩溃恢复
对于 Zookeeper 节点崩溃的情况,由于它使用了临时有序节点,并且这些节点与客户端会话绑定,因此当客户端崩溃时,其会话会超时,导致临时节点被删除。这确保了即使客户端崩溃,锁也能被正确释放,其他客户端可以安全地获取锁。
综上所述,Redis RedLock 因其基于内存和可能存在的持久化延迟,存在锁状态不一致的风险,而 Zookeeper 通过其强一致性的设计和数据持久化机制,提供了更高的锁一致性保障。
Martin 和 antirez 两位大佬就 RedLock 是否安全在很多年前展开过一次讨论,那次讨论很有名,网上也有大把的解读。
非要说一个结论的话就是:所有带超时机制的分布式锁,都无法在某个节点取得锁之后完美解决 NPC 问题。因此 Zookeeper 同样也不是百分百安全的。
P.S. NPC 即 Network Delay, Process Pause, Clock Drift 三个分布式问题的首字母缩写。