Redis红锁

在算法的分布式版本中,我们假设我们有N个Redis主节点。这些节点是完全独立的,因此我们不使用复制或任何其他隐式协调系统。我们已经描述了如何在单个实例中安全地获取和释放锁。我们理所当然地认为,算法将使用此方法在单个实例中获取和释放锁。在我们的示例中,我们设置了 N=5,这是一个合理的值,因此我们需要在不同的计算机或虚拟机上运行 5 个 Redis 主节点,以确保它们以一种基本独立的方式失败。

为了获取锁,客户端执行以下操作:

  1. 它获取当前时间(以毫秒为单位)。
  2. 它尝试按顺序获取所有 N 个实例中的锁,在所有实例中使用相同的键名和随机值。在步骤 2 中,在每个实例中设置锁定时,客户端使用与总锁定自动释放时间相比较小的超时来获取它。例如,如果自动释放时间为 10 秒,则超时可能在 ~ 5-50 毫秒范围内。这可以防止客户端长时间试图与已关闭的Redis节点通信:如果实例不可用,我们应该尽快尝试与下一个实例通信。
  3. 客户端通过从当前时间中减去步骤 1 中获得的时间戳来计算获取锁所经过的时间。当且仅当客户端能够在大多数实例(至少 3 个)中获取锁,并且获取锁所经过的总时间小于锁的有效时间,则该锁被视为已获取。
  4. 如果已获取锁,则其有效性时间被视为初始有效时间减去经过的时间,如步骤 3 中计算的那样。
  5. 如果客户端由于某种原因未能获取锁定(它无法锁定 N/2+1 个实例或有效期为负数),它将尝试解锁所有实例(甚至是它认为无法锁定的实例)。

算法是异步的吗?

该算法依赖于以下假设:虽然进程之间没有同步时钟,但每个进程中的本地时间以大致相同的速率更新,与锁的自动释放时间相比,误差幅度很小。这个假设与现实世界的计算机非常相似:每台计算机都有一个本地时钟,我们通常可以依靠不同的计算机来具有很小的时钟漂移。

在这一点上,我们需要更好地指定我们的互斥规则:只要持有锁的客户端在锁有效时间内(如步骤3中获得的那样)终止其工作,减去一些时间(只需几毫秒,以补偿进程之间的时钟漂移),它才能得到保证。

本文包含有关需要绑定时钟漂移的类似系统的更多信息:Leases:分布式文件缓存一致性的高效容错机制

失败时重试

当客户端无法获取锁时,它应该在随机延迟后重试,以便尝试取消同步多个客户端,尝试同时获取同一资源的锁(这可能会导致没有人获胜的裂脑情况)。此外,客户端在大多数 Redis 实例中尝试获取锁的速度越快,裂脑情况的窗口就越小(并且需要重试),因此理想情况下,客户端应尝试使用多路复用同时将 SET 命令发送到 N 个实例。

值得强调的是,对于未能获取大多数锁的客户端来说,尽快释放(部分)获取的锁是多么重要,这样就不需要等待密钥到期才能再次获取锁(但是,如果发生网络分区并且客户端不再能够与 Redis 实例通信, 在等待密钥过期时需要支付可用性罚款)。

释放锁

释放锁很简单,无论客户端是否认为它能够成功锁定给定实例,都可以执行。

安全论据

算法安全吗?让我们来看看在不同场景中会发生什么。

首先,我们假设客户端能够在大多数实例中获取锁。所有实例都将包含一个具有相同生存时间的密钥。但是,密钥是在不同的时间设置的,因此密钥也会在不同的时间过期。但是,如果在时间 T1(我们在联系第一台服务器之前采样之前采样的时间)将第一个键设置为最差,而在时间 T2(我们从最后一个服务器获得回复的时间)将最后一个键设置为最差,则我们确信集中第一个过期的密钥将至少存在 。所有其他密钥稍后将过期,因此我们确信至少这次将同时设置这些密钥。MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT

在设置大多数密钥期间,另一个客户端将无法获取锁,因为如果 N/2+1 密钥已存在,则 N/2+1 SET NX 操作无法成功。因此,如果获得了锁,则不可能同时重新获得它(违反互斥属性)。

但是,我们还希望确保尝试同时获取锁的多个客户端无法同时成功。

如果客户端锁定大多数实例的时间接近或大于锁定最大有效时间(我们基本上用于 SET 的 TTL),它将认为锁定无效并将解锁实例,因此我们只需要考虑客户端能够在小于有效时间的时间内锁定大多数实例的情况。在这种情况下,对于上面已经表达的参数,因为任何客户端都不应该能够重新获取锁。因此,仅当锁定大多数实例的时间大于 TTL 时间时,多个客户端才能同时锁定 N/2+1 个实例(“时间”是步骤 2 的结束),从而使锁定无效。MIN_VALIDITY

活泼性参数

系统活动性基于三个主要功能:

  1. 自动释放锁(因为钥匙过期):最终钥匙再次可供锁定。
  2. 事实上,通常,当未获取锁或获取锁并终止工作时,客户端将合作移除锁,因此我们可能不必等待密钥过期即可重新获取锁。
  3. 事实上,当客户端需要重试锁时,它等待的时间比获取大多数锁所需的时间要长得多,以便从概率上使资源争用期间的裂脑条件变得不太可能。

但是,我们支付的可用性损失等于网络分区上的TTL时间,因此,如果有连续分区,我们可以无限期地支付此罚款。每次客户端获取锁并在能够删除锁之前进行分区时,都会发生这种情况。

基本上,如果存在无限连续的网络分区,则系统可能会在无限长的时间内变得不可用。

性能、崩溃恢复和同步

许多使用 Redis 作为锁服务器的用户在获取和释放锁的延迟以及每秒可以执行的获取/释放操作数方面都需要高性能。为了满足这一要求,与N Redis服务器对话以减少延迟的策略肯定是多路复用(将套接字置于非阻塞模式,发送所有命令,稍后读取所有命令,假设客户端和每个实例之间的RTT相似)。

但是,如果我们想以崩溃恢复系统模型为目标,则关于持久性还有另一个考虑因素。

基本上,为了看到这里的问题,让我们假设我们配置Redis时根本没有持久性。客户端在 5 个实例中的 3 个实例中获取锁。客户端能够获取锁的其中一个实例重新启动,此时我们可以为同一资源锁定3个实例,而另一个客户端可以再次锁定它,这违反了锁的独占性的安全属性。

如果我们启用AOF持久性,事情将会有所改善。例如,我们可以通过向服务器发送 SHUTDOWN 命令并重新启动它来升级服务器。由于 Redis 过期在语义上是实现的,因此当服务器关闭时,时间仍然会过去,因此我们所有的要求都很好。但是,只要它是干净关闭,一切都很好。停电怎么办?如果 Redis 配置为(默认情况下)每秒在磁盘上同步一次,则重新启动后,我们的密钥可能会丢失。从理论上讲,如果我们想在面对任何类型的实例重启时保证锁定安全,我们需要在持久性设置中启用。由于额外的同步开销,这将影响性能。fsync=always

然而,事情比乍一看要好。基本上,只要实例在崩溃后重新启动,它就不再参与任何当前活动的锁定,算法安全性就会保留。这意味着实例重新启动时的当前活动锁定集都是通过锁定重新加入系统的实例以外的实例而获得的。

为了保证这一点,我们只需要在崩溃后使一个实例不可用,至少比我们使用的最大TTL多一点。这是实例崩溃时存在的有关锁的所有密钥变得无效并自动释放所需的时间。

使用延迟重启,即使没有任何可用的Redis持久性,基本上也可以实现安全,但请注意,这可能会转化为可用性损失。例如,如果大多数实例崩溃,系统将全局不可用 TTL(此处全局意味着在此期间根本没有资源可锁定)。

使算法更可靠:扩展锁

如果客户端执行的工作由小步骤组成,则默认情况下可以使用较小的锁定有效期,并扩展实现锁定扩展机制的算法。基本上,如果客户端在计算过程中锁定有效性接近较低值,则可以通过将Lua脚本发送到所有实例来扩展锁定,该实例扩展密钥的TTL(如果密钥存在并且其值仍然是客户端在获取锁定时分配的随机值)。

只有当客户端能够将锁扩展到大多数实例中,并且在有效时间内(基本上要使用的算法与获取锁时使用的算法非常相似),才应考虑重新获取锁。

但是,这在技术上不会更改算法,因此应限制锁定重新获取尝试的最大次数,否则会违反其中一个活动属性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容