分布式锁深度剖析:ZooKeeper(CP)与 Redis(AP)的实现原理与对比

张开发
2026/4/16 11:15:07 15 分钟阅读

分享文章

分布式锁深度剖析:ZooKeeper(CP)与 Redis(AP)的实现原理与对比
分布式锁深度剖析ZooKeeperCP与 RedisAP的实现原理与对比在分布式系统中锁是协调多个进程对共享资源互斥访问的基础工具。本文将深入分析 ZooKeeper 和 Redis 两种主流分布式锁的实现方案结合 CAP 理论对比它们的特性并探讨极端情况下的问题与对策。一、CAP 理论分布式锁的设计基石在开始之前我们先回顾 CAP 理论它解释了分布式系统在面临网络分区时的取舍特性含义说明C一致性所有节点在同一时刻看到相同的数据写操作完成后读操作必须返回最新值A可用性服务始终可用每个请求都能收到非错误响应不保证数据最新但保证不超时P分区容错性系统在网络分区节点间通信中断时仍能继续运行分布式系统必须选 P然后在 C 和 A 之间权衡CP 系统放弃可用性保证强一致性和分区容错性如 ZooKeeper、etcdAP 系统放弃强一致性保证高可用和分区容错性如 Redis 主从架构 分布式锁需要强一致性吗通常需要 —— 如果锁数据不一致可能导致多个客户端同时进入临界区。但 Redis 为了性能选择了 AP 风格所以会有丢锁风险。二、ZooKeeper 实现分布式锁CP 型2.1 基础版本临时节点 Watch存在羊群效应ZooKeeper 的数据模型是树形目录ZNode支持临时节点会话结束时自动删除和Watch 机制监听节点变化。加锁思路在锁目录下创建一个临时节点比如/locks/mutex。创建成功的客户端获得锁。其他客户端 watch 该节点当节点被删除时锁释放所有监听客户端同时收到通知然后抢占创建节点。问题羊群效应Herd Effect当锁释放时大量客户端被唤醒并尝试创建节点但只有一个能成功其余失败后会再次 watch导致 ZooKeeper 瞬时压力巨大网络开销急剧增加。2.2 改进方案临时顺序节点避免羊群效应核心思想让所有客户端创建临时顺序节点如/locks/lock_0000000001节点序号全局递增。谁创建的节点序号最小谁持有锁。工作流程是否客户端请求加锁在锁目录下创建临时顺序节点/locks/lock_xxx获取锁目录下所有子节点并排序自己是否序号最小?获得锁执行业务watch 序号比自己小的前一个节点等待前一个节点删除事件收到删除通知重新判断业务完成删除自己节点释放锁为什么避免了羊群效应每个客户端只 watch 前一个节点锁释放时只通知下一个节点或少数节点而不是所有客户端。ZooKeeper 锁的优缺点优点缺点强一致性锁安全可靠性能相对较低每次操作需要 ZooKeeper 集群多数确认客户端宕机自动释放临时节点需要维护长连接会话无羊群效应公平锁实现比 Redis 复杂三、Redis 实现分布式锁AP 型3.1 早期方案SETNX EXPIRE非原子有风险SETNX lock_key1EXPIRE lock_key30问题如果 SETNX 后客户端崩溃EXPIRE 未执行锁永远不释放。改进使用SET key value NX EX seconds原子命令Redis 2.6.12。3.2 Redisson 实现Lua WatchDogRedisson 是 Redis 官方推荐的 Java 分布式锁实现内部通过Lua 脚本保证原子性并提供WatchDog自动续期。加锁流程简化版WatchDogRedis单机/主节点Redisson客户端WatchDogRedis单机/主节点Redisson客户端loop[锁未释放且客户端存活]alt[加锁成功]EVAL Lua脚本判断锁是否存在不存在则设置并返回1成功返回1 / 失败返回0启动 WatchDog 线程每10秒执行EVAL 续期Lua将锁过期时间重置为30秒OKLua 脚本核心逻辑伪代码ifredis.call(exists,KEYS[1])0thenredis.call(hset,KEYS[1],ARGV[2],1)redis.call(pexpire,KEYS[1],ARGV[1])return1elseifredis.call(hexists,KEYS[1],ARGV[2])1thenredis.call(hincrby,KEYS[1],ARGV[2],1)redis.call(pexpire,KEYS[1],ARGV[1])return1elsereturn0endendWatchDog 续期机制默认锁超时时间 30 秒。每 10 秒检查一次锁超时时间的三分之一如果客户端仍持有锁则重置过期时间为 30 秒。客户端宕机 → WatchDog 线程消失 → 锁到期自动释放不会永久死锁。3.3 Redis 主从架构下的丢锁问题致命缺陷场景Redis 采用主从 哨兵模式AP 架构1. 客户端向 Master 写入锁 key成功 2. Master 宕机数据尚未同步到 Slave 3. 哨兵选举新 Master原来的 Slave 升为主 4. 新 Master 中没有锁 key 5. 另一个客户端可以成功加锁 → 两个客户端同时持有锁 ❌这就是 AP 系统放弃一致性带来的严重后果。3.4 RedLock 方案及其争议为了修复主从丢锁问题Redis 作者提出RedLock部署至少 3 个独立的 Redis 主节点非主从无复制。客户端依次向所有节点请求加锁使用相同的 key 和随机值。当超过半数节点N/21加锁成功且总耗时小于锁有效时间才认为加锁成功。释放锁时向所有节点发送删除命令。RedLock 的问题Martin Kleppmann 等专家指出依赖系统时钟锁的有效性依赖各节点时钟一致时钟跳跃可能导致锁失效。垃圾回收GC停顿客户端 GC 期间锁可能已过期被其他客户端获取。网络延迟复杂的同步逻辑性能不如单节点 Redisson。AOF 持久化问题即使使用 RedLock如果节点 AOF 未 fsync 就宕机重启后可能丢失锁数据。因此生产环境中绝大多数 Redis 分布式锁直接使用 Redisson 单节点或主从模式并接受极端情况下的丢锁风险比如用于非关键业务或配合业务回滚机制。四、ZooKeeper vs Redis 分布式锁对比对比维度ZooKeeperCPRedis RedissonAP一致性强一致性ZAB 协议最终一致性主从复制延迟可能丢锁可用性较低选举期间不可用高主从切换快或单节点一直可用性能较低每次操作需多数确认极高内存操作 单线程锁释放临时节点会话结束自动删主动删除 超时释放 WatchDog 续期公平性有序节点保证公平先请求先得非公平锁Redisson 默认非公平实现复杂度较复杂需管理会话、Watch简单Redisson 封装完善典型场景对安全性要求极高的场景如选主、配置管理高并发、高性能场景如秒杀、防重复提交五、选型建议场景推荐方案原因金融、交易系统ZooKeeper / etcd锁丢失可能造成资损必须 CP高并发秒杀Redis 单节点Redisson性能第一丢锁概率低可配合业务幂等跨机房部署ZooKeeperCPRedis 主从同步跨机房延迟高丢锁风险增大简单防重复RedisSET NX EX无需 WatchDog够用就好⚠️ 注意无论使用哪种锁业务层都应设计幂等性作为最后一道防线。六、总结ZooKeeper 锁通过临时顺序节点 Watch 实现了公平、安全的 CP 锁避免了羊群效应但性能相对较低。Redis 锁借助 Lua 原子性和 WatchDog 实现了高性能 AP 锁但在主从架构下存在丢锁风险RedLock 试图修复但仍有争议。CAP 理论帮我们理解两种锁的取舍ZooKeeper 优先保证一致性与分区容错Redis 优先保证可用性与分区容错。分布式锁没有银弹根据业务对一致性和性能的敏感度做出选择才是最佳实践。参考资料《Redis 设计与实现》Redisson 官方文档ZooKeeper 官方文档Martin Kleppmann 对 RedLock 的批评文章

更多文章