ZooKeeper vs Redis:分布式锁的实现与选型指南
一、Redis 分布式锁:追求极致的性能
Redis 分布式锁基于内存操作,其核心思想是在内存中设置一个唯一的键值对来表示锁的持有。
1. 基础实现(SETNX + Lua)
最简单的实现是使用 SETNX(SET if Not eXists)命令:
加锁:设置一个键,值为随机值,并设置过期时间(防止死锁)
SET lock_resource_name my_random_value NX PX 30000
#解锁:使用Lua脚本保证原子性(检查值再删除)
if redis.call(“get”, KEYS[1]) == ARGV[1] then
return redis.call(“del”, KEYS[1])
else
return 0
end
2. 生产环境推荐(Redisson框架)
在实际生产中,强烈推荐使用 Java 的 Redisson 库,它解决了基础实现的诸多痛点:
• 看门狗机制(Watchdog):异步续期线程,在业务执行期间自动为锁续期,避免业务未完成而锁自动过期的问题。
• 可重入锁:支持同一线程多次加锁。
• Lua脚本原子性:所有操作都使用Lua脚本,保证原子性。
// Redisson 示例
RLock lock = redissonClient.getLock(“myLock”);
try {
lock.lock();
// … 执行业务逻辑
} finally {
lock.unlock();
}
3. Redis锁的优势与劣势
• 优势:
-
性能极高:基于内存操作,吞吐量大,延迟低,适用于高并发场景(如秒杀)。
-
实现简单:API 直观易懂,部署方便。
-
功能丰富:通过 Redisson 支持多种锁类型(可重入、公平、读写锁)。
• 劣势:
-
一致性依赖:在 Redis 主从异步复制架构下,如果主节点宕机且锁信息未同步到从节点,可能导致锁失效,出现多个客户端同时持有锁的情况(尽管 Redlock 算法试图解决此问题,但仍有争议)。
-
非强一致性:本质上是 AP 系统,优先保证可用性,在网络分区时可能牺牲一致性。
二、ZooKeeper 分布式锁:追求绝对的可靠
ZooKeeper 是一个分布式协调服务,其分布式锁基于 临时顺序节点 和 Watch 机制,实现了强一致性。
1. 核心实现(临时顺序节点 + Watch)
• 加锁:所有客户端在同一个锁目录(如 /locks/mylock)下创建临时顺序节点。
• 排队:客户端获取目录下所有子节点,判断自己创建的节点是否为序号最小的。
• 监听:如果不是最小的,客户端只需监听(Watch)比自己序号小的前一个节点的删除事件,而无需监听所有节点。
• 解锁:当前一个节点被删除(即锁被释放)时,ZooKeeper 会精准通知下一个客户端,使其获得锁。
2. 生产环境推荐(Curator框架)
Apache Curator 库封装了 ZooKeeper 的复杂逻辑,提供了开箱即用的分布式锁。
// Curator 示例
InterProcessMutex lock = new InterProcessMutex(client, “/locks/mylock”);
try {
if (lock.acquire(10, TimeUnit.SECONDS)) {
// … 执行业务逻辑
}
} finally {
lock.release();
}
- ZooKeeper锁的优势与劣势
• 优势:
• 强一致性(CP):基于 ZAB 协议,数据在集群内强一致,锁模型非常可靠,不会出现脑裂导致的双重加锁。
• 自动释放:锁与客户端 Session 绑定,如果客户端宕机,其创建的临时节点会自动删除,锁也随之释放,有效避免了死锁。
• 公平锁:节点顺序生成,天然实现了公平的先来后到机制。
• 无惊群效应:通过 Watch 机制精准通知下一个等待者,避免了同时争抢。
• 劣势:
• 性能较低:每次加解锁都需要创建、删除节点,涉及网络通信和磁盘写入(日志),性能远低于 Redis。
• 运维复杂:需要额外维护一个 ZooKeeper 集群,增加了系统复杂度。
三、核心差异对比一览表
特性维度 Redis 分布式锁 ZooKeeper 分布式锁
一致性模型 最终一致性 (AP) 强一致性 (CP)
性能 高 (内存操作) 低 (需持久化日志)
可靠性 依赖配置和算法 (如 Redlock) 高 (基于 ZAB 协议)
锁释放 依赖超时 (手动或看门狗) 自动释放 (临时节点)
锁类型 非公平锁 (可配置公平锁) 公平锁 (顺序节点)
惊群效应 可能发生 可避免 (精准Watch)
运维成本 低 (通常已有Redis) 高 (需维护ZK集群)
四、选型建议:如何选择?
选择没有绝对的对错,只有适合与否。
选择 Redis 锁的场景:
• 高并发、高性能需求是首要目标(如秒杀、抢购、缓存击穿)。
• 可以接受极小概率下的锁失效(如业务逻辑有幂等性等补偿措施)。
• 系统已经部署了 Redis,希望减少外部依赖。
选择 ZooKeeper 锁的场景:
• 绝对可靠比性能更重要(如金融交易、核心账务、主备选举)。
• 锁持有时间较长的业务流程,不希望引入复杂的超时和续期机制。
• 系统已经依赖 ZooKeeper 做其他协调服务(如配置中心、服务发现)。
一言以蔽之:追求极致性能用 Redis,追求绝对可靠用 ZooKeeper。