关于Redis RedLock算法的争论

栏目: 数据库 · 发布时间: 5年前

内容简介:Martin上来就问,我们要锁来干啥呢?2个原因:对于第1种原因,我们对锁是有一定宽容度的,就算发生了两个节点同时工作,对系统的影响也仅仅是多付出了一些计算的成本,没什么额外的影响。这个时候 使用对于第2种原因,对正确性严格要求的场景(比如订单,或者消费),就算使用了 RedLock 算法仍然

Martin上来就问,我们要锁来干啥呢?2个原因:

  1. 提升效率,用锁来保证一个任务没有必要被执行两次。比如(很昂贵的计算)
  2. 保证正确,使用锁来保证任务按照正常的步骤执行,防止两个节点同时操作一份数据,造成文件冲突,数据丢失。

对于第1种原因,我们对锁是有一定宽容度的,就算发生了两个节点同时工作,对系统的影响也仅仅是多付出了一些计算的成本,没什么额外的影响。这个时候 使用 单点的 Redis 就能很好的解决问题,没有必要使用RedLock,维护那么多的 Redis 实例,提升系统的维护成本。

对于第2种原因,对正确性严格要求的场景(比如订单,或者消费),就算使用了 RedLock 算法仍然 不能保证锁的正确性

我们分析一下 RedLock 的有啥缺陷吧:

关于Redis RedLock算法的争论

作者 Martin 给出这张图,首先我们上一讲说过,RedLock中,为了防止死锁,锁是具有过期时间的。这个过期时间被 Martin 抓住了小辫子。

  • 如果 Client 1 在持有锁的时候,发生了一次很长时间的 FGC 超过了锁的过期时间。锁就被释放了。
  • 这个时候 Client 2 又获得了一把锁,提交数据。
  • 这个时候 Client 1 从 FGC 中苏醒过来了,又一次提交数据。

这还了得,数据就发生了错误。RedLock 只是保证了锁的高可用性,并没有保证锁的正确性。

这个时候也许你会说,如果 Client 1 在提交任务之前去查询一下锁的持有者是不自己就能解决这个问题?

答案是否定的,FGC 会发生在任何时候,如果 FGC 发生在查询之后,一样会有如上讨论的问题。

那换一个没有 GC 的编程语言?

答案还是否定的, FGC 只是造成系统停顿的原因之一,IO或者网络的堵塞或波动都可能造成系统停顿。

Martin给出了一个解决的方案:

关于Redis RedLock算法的争论

为锁增加一个 token-fencing。

  • 获取锁的时候,还需要获取一个递增的token,在上图中 Client 1 还获得了一个 token=33的 fencing。
  • 发生了上文的 FGC 问题后,Client 获取了 token=34 的锁。
  • 在提交数据的时候,需要判断token的大小,如果token 小于 上一次提交的 token 数据就会被拒绝。

我们其实可以理解这个 token-fencing 就是一个乐观锁,或者一个 CAS。

Martin 还指出了,RedLock 是一个 严重依赖系统时钟 的分布式系统。

还是这个过期时间的小辫子。如果某个 Redis Master的系统时间发生了错误,造成了它持有的锁提前过期被释放。

  • Client 1 从 A、B、D、E五个节点中,获取了 A、B、C三个节点获取到锁,我们认为他持有了锁
  • 这个时候,由于 B 的系统时间比别的系统走得快,B就会先于其他两个节点优先释放锁。
  • Clinet 2 可以从 B、D、E三个节点获取到锁。在整个分布式系统就造成 两个 Client 同时持有锁了。

这个时候 Martin 又提出了一个相当重要的关于分布式系统的设计要点:

好的分布式系统应当是异步的,且不能时间作为安全保障的。因为在分布式系统中有会程序暂停,网络延迟,系统时间错误,这些因数都不能影响分布式系统的安全性,只能影响系统的活性(liveness property)。换句话说,就是在极端情况下, 分布式系统顶多在有限的时间内不能给出结果,但是不能给出错误的结果

所以总结一下 Martin 对 RedLock 的批评:

  • 对于提升效率的场景下,RedLock 太重。
  • 对于对正确性要求极高的场景下,RedLock 并不能保证正确性。

这个时候感觉醍醐灌顶,简直写的太好了。

RedLock 的作者,同时也Redis 的作者对 Martin的文章也做了回应,条理也是相当的清楚。

antirez 的回应

antirez 看到了 Martin 的文章以后,就写了一篇文章回应。剧情会不会反转呢?

antirez 总结了 Martin 对 RedLock的指控:

  1. 分布式的锁具有一个自动释放的功能。锁的互斥性,只在过期时间之内有效,锁过期释放以后就会造成多个Client 持有锁。
  2. RedLock 整个系统是建立在,一个在实际系统无法保证的系统模型上的。在这个例子中就是系统假设时间是同步且可信的。

对于第一个问题:

antirez 洋洋洒洒的写了很多,仔细看半天,也没有解决我心中的疑问。回顾一下RedLock 获取锁的步骤:

  1. 获取开始时间
  2. 去各个节点获取锁
  3. 再次获取时间。
  4. 计算获取锁的时间,检查获取锁的时间是否小于获取锁的时间。
  5. 持有锁,该干啥干啥去

如果,程序在1-3步之间发生了阻塞,RedLock可以感知到锁已经过期,没有问题。

如果,程序在第 4 步之后发生了阻塞?怎么办???

答案是,其他 具有自动释放锁的分布式锁都没办解决这个问题

对于第二个质疑:

antirez 认为,首先在实际的系统中,从两个方面来看:

  1. 系统暂停,网络延迟。
  2. 系统的时间发生阶跃。

对于第1个问题。上文已经提到了,RedLock做了一些微小的工作,但是没办法完全避免。其他带有自动释放的分布式锁也没有办法。

第2个问题,Martin认为系统时间的阶跃主要来自两个方面:

  • 人为修改。
  • 从NTP服务收到了一个跳跃时时钟更新。

对于人为修改,能说啥呢?人要搞破坏没办法避免。

NTP受到一个阶跃时钟更新,对于这个问题,需要通过运维来保证。需要将阶跃的时间更新到服务器的时候,应当采取小步快跑的方式。多次修改,每次更新时间尽量小。

所以严格来说确实, RedLock建立在了 Time 是可信的模型上,理论上 Time 也是发生错误,但是在现实中,良好的运维和工程一些机制是可以最大限度的保证 Time 可信。

最后, antirez 还打出了一个暴击,既然 Martin 提出的系统使用 fecting token 保证数据的顺序处理。还需要 RedLock,或者别的分布式锁干啥??

回顾

每一个系统设计都有自己的侧重或者局限。工程也不是完美的。在现实中工程中不存在完美的解决方案。我们应当深入了解其中的原理,了解解决方案的优缺点。明白选用方案的局限性。是否可以接受方案的局限带来的后果。

架构本来就是一门平衡的艺术。

简单来说,单实例Redis能解决大部分的分布式锁需求。Redlock的引入意义不大,如果对可用性要求更高的话,使用其他方案也许是更好的选择。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ANSI Common Lisp

ANSI Common Lisp

Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40

For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具