Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > redis 红锁

redis中红锁的使用小结

作者:悟能不能悟

RedLock 是 Redis 官方提出的一种用于在分布式环境下实现强一致性分布式锁的算法,本文就来详细的介绍一下redis中红锁的使用小结,感兴趣的可以了解一下

好的,我们来详细、深入地探讨一下 Redis 红锁(RedLock)。

RedLock 是 Redis 官方提出的一种用于在分布式环境下实现强一致性分布式锁的算法。它的提出,是为了解决在 Redis ​主从复制哨兵模式下,使用单实例 Redis 锁可能遇到的锁失效问题。

1. 为什么需要红锁?—— 问题的起源

在深入了解红锁之前,必须先理解它要解决什么问题。

假设我们使用单个 Redis 实例实现分布式锁(通常用 SET key random_value NX PX 30000命令):

  1. 客户端 A 在 Master 节点上成功获取锁。
  2. 在 Master 将锁数据同步到 Slave 节点之前,Master 宕机了。
  3. 哨兵机制触发,一个 Slave 节点升级为新的 Master。
  4. 此时,这个新的 Master 节点上并没有客户端 A 持有的锁。
  5. 客户端 B 向新的 Master 节点申请锁,成功获取。此时,​客户端 A 和客户端 B 同时认为自己持有了锁,导致锁的安全性被破坏。

核心问题​:在异步复制的场景下,锁数据在写入主节点后,到同步到从节点之间存在一个时间窗口。在这个窗口内主节点故障,会导致锁数据的丢失。

红锁的目标就是消除对单个 Redis 实例的依赖,从而避免这类问题。

2. 红锁算法(RedLock Algorithm)的核心思想

红锁的基本思想非常直观:​​“不要把所有鸡蛋放在一个篮子里”​

它要求客户端向一个独立的、无主从关系的 Redis 实例集群中的多数(N/2+1)个节点依次申请锁,只有当从多数节点都获取锁成功,并且总耗时小于锁的有效时间,才算最终加锁成功。

算法前提条件

部署多个 Redis Master 节点​:这些节点必须是完全独立的,相互之间没有数据同步关系(例如,不是同一个哨兵或集群下的节点)。建议至少 5 个节点,这样可以容忍其中 2 个节点故障,从而保证系统的可用性。

算法步骤详解

假设我们有 N 个 Redis 节点(例如 N=5)。

第一步:获取当前时间

客户端在开始获取锁之前,先记录一个开始时间 start_time

第二步:依次向所有 N 个节点申请锁

客户端使用相同的键名和随机值,依次向 5 个 Redis 实例发送锁申请命令(SET lock_name my_random_value NX PX 30000)。

第三步:计算获取锁的总耗时

当客户端从所有节点都收到响应(无论是成功还是失败)后,记录结束时间 end_time。计算总耗时:total_time = end_time - start_time

第四步:检查锁是否获取成功

客户端需要同时满足以下两个条件,才认为锁获取成功:

  1. 多数派原则​:客户端从至少 N/2 + 1个节点(对于 5 个节点,就是 3 个)上成功获取了锁。
  2. 有效性检查​:获取锁的总耗时 total_time必须小于锁的自动释放时间(TTL)。例如,锁的 TTL 是 10 秒,而 total_time是 2 秒,那么是有效的。如果 total_time是 12 秒,则无效。

为什么需要条件 2?​​ 这是为了防止客户端在获取锁的过程中耗时太久,导致最早申请到的那些锁在客户端开始执行关键代码前就已经过期了。

如果锁获取失败​:客户端必须向所有 Redis 节点发起释放锁的请求​(即执行 Lua 脚本,检查随机值并删除锁)。即使某些节点返回失败(比如它本来就没加锁成功),也需要尝试释放,以确保清理现场。

3. 释放锁

释放锁的过程相对简单:客户端需要向第一步中尝试获取锁的所有 N 个节点发送释放命令。

释放命令必须使用 Lua 脚本保证原子性:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

这样做是为了确保只有锁的持有者才能释放锁,避免误删其他客户端创建的锁。

4. 红锁的争议与局限性(非常重要!)

RedLock 自提出以来,就在分布式系统领域引发了激烈的讨论,特别是来自 Martin Kleppmann(《数据密集型应用系统设计》作者)的挑战。理解这些争议点对于正确使用红锁至关重要。

主要争议点:

对系统时钟(时钟跳跃)的敏感性

GC Pause(垃圾回收暂停)或进程暂停带来的安全性问题

场景(Martin 描述的著名例子)​​:

问题的本质​:红锁(以及任何基于超时的锁)无法区分“客户端业务逻辑执行缓慢”和“客户端进程已崩溃”这两种情况。它依赖于超时机制,而超时机制在存在长时间进程暂停时就会失效。

解决方案的讨论​:Martin 提出需要使用一种能够在共享存储中留下“围栏令牌”(fencing token)​​ 的锁服务。客户端在写数据时,需要检查一个单调递增的令牌,以确保自己的操作是在最新的锁状态下进行的。这实际上要求共享资源层(如 Zookeeper、etcd)提供额外的协调能力。

5. 红锁的使用建议

考虑到上述争议,你应该在以下情况下考虑使用红锁:

替代方案​:

总结

特性

描述

目标

在分布式 Redis 环境下实现一个更安全、强一致的分布式锁。

核心思想

向多个独立的 Redis 主节点申请锁,遵循“多数派”原则。

优点

解决了单点 Redis 和主从架构下因异步复制导致的锁失效问题。

缺点/争议

1. 对系统时钟漂移敏感。
2. 无法完全解决 GC Pause 等进程暂停问题。
3. 部署更复杂,性能低于单节点锁。

适用场景

对锁的一致性要求非常高,且愿意为了一致性牺牲部分性能和增加复杂度的场景。

替代方案

Redis 主从锁(可接受风险)、ZooKeeper、etcd。

到此这篇关于redis中红锁的使用小结的文章就介绍到这了,更多相关redis 红锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文