专栏首页工作笔记精华关于Redis RedLock算法的争论

关于Redis RedLock算法的争论

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

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

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

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

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

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

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

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

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

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

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

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

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

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

为锁增加一个 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的引入意义不大,如果对可用性要求更高的话,使用其他方案也许是更好的选择。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • hdfs远程连接异常 原

    stys35
  • hdfs伪分布式搭建,伪分布式模式下yarn的配置及测验

    stys35
  • 在hbase 激活kerberos 下opentsdb的使用

    最近公司大数据集群统一升级了 kerberos,那原先 的opentsdb就不能使用了,需要使用keytab方式登陆验证。

    stys35
  • PHP实现的权重算法示例【可用于游戏根据权限来随机物品】

    要随机取出其中一条,并且不是完全随机,而是根据其中weight的数值,按照数值越大,几率越高的规律取出。

    砸漏
  • 聊聊dubbo的DubboComponentScanRegistrar

    本文主要研究一下dubbo的DubboComponentScanRegistrar

    codecraft
  • 聊聊dubbo的DubboComponentScanRegistrar

    本文主要研究一下dubbo的DubboComponentScanRegistrar

    codecraft
  • 【笔记】微信小程序通过app.json设置底部导航

    borderStyle tabbar上边框的颜色, 仅支持 black/white

    德顺
  • 多集群真的不会管理!

    传统单一集群式的服务模式将会越来越暴露出它的局限性,包括它的单主节点性能瓶颈,异构环境下的服务模式等等,而多集群协同服务将会是大规模集群服务的一个重要演变趋势。

    CSDN技术头条
  • MyBatis XML简单理解

    其中,namespace用于绑定Mapper接口。不同mapper接口对应到不同的xml。

    小锋学长
  • 分布式ID常见解决方案

    在分布式系统中,往往需要对大量的数据如订单、账户进行标识,以一个有意义的有序的序列号来作为全局唯一的ID。

    黄泽杰

扫码关注云+社区

领取腾讯云代金券