关于分布式:聊一聊分布式锁的设计模型

39次阅读

共计 6637 个字符,预计需要花费 17 分钟才能阅读完成。

一、什么是分布式锁?

什么是分布式锁?对于这个问题,置信很多同学是既相熟又生疏。随着分布式系统的疾速倒退与广泛应用,针对共享资源的互斥拜访也成为了很多业务必须要面对的需要,这个场景下人们通常会引入分布式锁来解决问题。咱们通常会应用怎么样的散布锁服务呢?有开源的 MySQL,Redis,ZooKeeper,Etcd 等三方组件可供选择,当然也有团体内自研的 Tair,Nuwa 等分布式锁服务提供方。总的来看,咱们对分布式锁的需要能够大体划分为以下两类利用场景:

  • 实现操作原子性:在单机环境中,为了实现多过程或多线程对共享资源操作过程的原子性,咱们能够借助内核提供的 SpinLock 或 Mutex 机制,保障只有一个过程或线程操作共享资源。和单机环境对锁的需要相似,在分布式环境中,咱们通常会用分布式锁管制多个机器上的节点并发操作,防止数据或状态被毁坏。
  • 实现零碎高可用:为了服务的高可用,往往须要部署多个节点实现服务冗余,防止单点故障造成的服务不可用。借助分布式锁 + 服务发现实现的选主性能,节点依据抢锁胜利与否决定是否成为主节点对外提供服务。当产生节点宕机时,其余备份节点能够通过争抢到分布式锁的所有权,持续提供拜访服务。

分布式锁的业务需要、场景看起来比较简单,然而事实上咱们在应用分布式锁过程中,总还是会提出这样、那样的新需要,看起来找不到一个分布式锁场景的大一统的解决方案。那么,分布式锁外部到底是怎么实现的?或者说应该怎么实现呢?这个是咱们这篇文章心愿探讨的,也心愿咱们的探讨可能让读者敌人对分布式锁的原理有肯定理解,在做技术选型的时候,也可能有更多的领导。

二、设计模型

咱们应该给分布式锁建设怎么样的设计模型呢?这个问题能够换个视角来看,咱们应该建设怎么样的正当性质来打造出一个分布式锁模型?咱们无妨参考一下来自业界的两个定义。首先是 Apache Helix(开源社区一个风头正劲的通用集群资源管理框架,它能被用作主动治理存在于集群节点上的分区的,有正本的分布式资源)对于分布式锁管理器的性质定义:a)均匀分布,不是先开始的节点获取所有的分布式锁;b)再调度的均衡性,须要妥善处理持有分布式锁的节点意外退出后的锁资源分配问题;c)再均衡,当有新的节点退出的时候,节点间的锁资源应该再调配以达到平衡。看得出来,Helix 对分布式锁模型的定义十分强调均衡性,思考到它是负责集群内的分区资源调度的,这个侧重点并不让人意外。

图 1 Helix 提出的分布式锁管理器的性质

咱们再看另一个赫赫有名的 Redis 对分布式锁性质的定义,它提出了分布式锁模型必须要恪守的三个准则:a)相对互斥,同一时刻,只有一个客户端可能持有分布式锁;b)最终可用,如果持有分布式锁的客户端意外退出了,那么相干的分布式锁资源要可能被从新再调配;c)服务容错,提供分布式锁的服务自身要具备容错能力,即便局部节点解体,也不影响整体的分布式锁服务。

图 2 Redis 提出的分布式锁管理器的性质

联合本身的教训,咱们高度认同 Redis 对无关分布式锁模型的根本约束条件,这些其实也是实现一个分布式锁服务所必须要思考的几个属性。并且,Redis 相干的文章中也持续探讨了分布式锁的其它的个性束缚。事实上,如下图 3 所示,咱们从三个维度演绎总结一个分布式锁模型落地须要思考的性质。第一个维度是最根本的约束条件,与 Redis 提出的完全一致,咱们称之为:互斥性,可容错,最终可用;第二层提出的分布式锁管理器须要关注的一些锁的个性,譬如抢锁效率,分布式锁的均衡性,锁的切换精度,锁的可重入性质等等。在这个之上,还有一个分布式锁落地时候必须要思考的事关数据一致性与正确性保障的束缚,即可防护性以及应答好时钟漂移的影响。

图 3 分布式锁设计模型须要思考的三个维度的性质

对于分布式锁管理器理论落地须要思考的数据一致性与正确性的话题,其中一个话题是墙上工夫的不靠谱,这个能够引入非墙上工夫 MonoticTime 来解决,本文就不在这个问题上做更多探讨。另一个话题,理论应用分布式锁服务来访问共享资源的时候肯定要辅助以 Fencing 能力方可做到资源拜访的相对互斥性。大神 Martin Kleppmann 提供了一个十分好的案例阐明,如下图 4 所示,Client1 首先获取了分布式锁的所有权,在操作数据的时候产生了 GC,在长时间的 ”Stop-The-World” 的 GC 过程中失落了锁的所有权,Client2 争抢到了锁所有权,开始操作数据,后果等 Client1 的 GC 实现之后,就会呈现 Client1,Client2 同时操作数据的情景,造成数据不统一。

图 4 不足 Fencing 爱护的分布式锁可能导致数据不统一

针对上述问题,解决方案是引入共享资源拜访的 IO Fence 能力,如下图 5 所示,全局锁服务提供全局自增的 Token,Client1 拿到锁返回的 Token 是 33,并带入存储系统,产生 GC,当 Client2 抢锁胜利返回 Token 34,带入存储系统,存储系统会回绝后续 Token 小于 34 的申请,那么通过了长时间 GC 从新复原后的 Client 1 再次写入数据的时候,因为底层存储系统记录的 Token 曾经更新,携带 Token 值为 33 的申请会被间接回绝,从而达到了数据保护的成果。

图 5 基于 Fencing 的数据一致性保障

回到文章的宗旨,如何实现一个高效的分布式锁管理器呢?首先,抛出一个观点,分布式锁管理器也能够依照管制立体与数据立体进行切分。图 3 中提到的分布式锁性质能够划分到不同的立体别离负责。咱们的这个观点其实并非独创,事实上在 OSDI’20 的 Best Paper -《Virtual Consensus in Delos》一文,Facebook 的钻研团队针对一致性协定的设计做了深入探讨,十分的精彩。文章外面提到了相似 Raft 这类分布式一致性协定,外面也同样能够分拆出管控立体与数据立体,前者负责容错、成员变更、角色调整,后者负责定序与长久化。通过解耦两个立体,一下子让共识协定变得很灵便。

图 6 Delos 中 Virtual Consensus 对管控数据立体的观点

咱们分布式锁模型的实现是否也能够参考相似的思路呢?将容错、成员变更等负责的逻辑转移至管控立体,而数据立体负责分布式锁的其它譬如互斥,最终可用,抢锁效率等等性能。答案是必定的,好吧,即便这样的思路也并非咱们独创,在数据库畛域,始终有这么个流派来演进这类的分布式锁零碎,它们被统称为 DLM(Distributed Lock Manager),典型的有 Oracle RAC,GFS2,OCFS2,GPFS,咱们接下来好好说道说道 DLM。

三、何谓 DLM?

DLM 的思维来自《The VAX/VMS Distributed Lock Manager》,在 1984 年首次利用于 VAX/VMS V4.0。接下来,咱们以 Oracle RAC 为例,来阐明下 DLM 的设计思路。

Oracle RAC 运行于集群之上,基于内存交融技术,使得 Oracle 数据库具备高可用性和极致性能。如果集群内的一个节点产生故障,Oracle 能够持续在其余的节点上运行。为了保障多个节点写入内存 Page 过程的一致性,应用分布式锁管理器 (DLM) 解决分布式锁资源的调配和开释。

如图 7 所示,DLM 是一个去中心化的设计,集群中的所有节点都是对等的,每个节点都保护了局部锁信息。那么申请锁时,应该由谁来决定锁的调配呢 ? 在 DLM 中,每把锁都有 Master 的概念,由 Master 对立协调、受权,决定是否容许加锁或解锁,每个节点都有可能成为锁的 Master。每个节点治理这些锁资源时,将这些锁资源通过树状构造进行组织,通过对树节点的父子继承关系能够优化锁的粒度,晋升加解锁的效率。

图 7 DLM 的分布式锁角色关系

在加锁或解锁过程中,波及到以下几类节点: a)Requester: 发动加锁或解锁的节点;b)DirectoryNode: 锁的目录节点,寄存着锁的 Master 被哪个节点锁持有这类信息;d)Master: 锁的持有者,理论管理者,负责锁的调配,开释。上面咱们用具体示例来形容散布锁的调配、开释的具体过程,例子外面存在 A,B,C 3 个节点,其中 A 为 Requester,B 为 DirectoryNode, C 为 Master 节点。

3.1 加锁过程

图 8 是须要到其余节点上加锁的过程,是所有加锁状况中最耗时的状况,最多须要 2 轮交互。当资源在本地建设后,后续对于具备继承关系的资源在本地加锁就能够了,无需和其余节点进行交互:

1. 节点 A 对资源 R1 加锁,首先在本地结构该锁对象,也称为锁的 shadow,但此时 A 节点并未加锁胜利;

2. 节点 A,对资源 R1 通过哈希计算出 R1 对应的目录管理者为节点 B;

3. 节点 A 申请节点 B,节点 B 的记录上显示 R1 的锁的 Master 在 C 上;

4. 节点 A 向节点 C 发动对 R1 加锁申请;

5. 节点 C 保护 R1 的锁申请队列,如果容许 A 加锁,则返回胜利;

6. A 更新本地 R1 锁 shadow 相干信息,加锁实现。

图 8 DLM 的加锁过程

3.2 解锁过程

图 9 展现理解锁的过程,也比拟直观,如下三个步骤:

1. 节点 A 对资源 R1 解锁,删除本地结构该锁对象;

2. 节点 A 申请节点 C,申请将 A 的锁开释;

3. 若 A 是队列中最初一个请求者,则节点 C 将发送申请给 B,将 R1 从目录中摘除,以便后续其余节点可能成为锁的 Master,否则,C 节点仅仅将 A 从 R1 的加锁队列移除。

图 9 DLM 的解锁过程

3.3 成员变更

上述的加锁和解锁过程,仅仅是一般的一次加解锁过程。那么集群呈现节点故障、集群增删节点,如何管制分布式锁可能被失常路由和调配呢?在 DLM 中,存在 Connection Manager 角色,除了负责各个节点的网络通信,还有一个重要性能是在集群节点产生增删时,节点间首先选举出 leader 节点进行协调,每个节点均有可能成为 leader 节点。在产生节点减少或删除时会下述过程:

  • 重建节点拓补:leader 节点通过两阶段投票形式向集群其余节点发动通告,告知以后集群节点拓扑状况,其余节点有权力承受或回绝该信息。若对该拓扑图未达成统一(其余节点回绝该拓扑信息),leader 会休眠一段时间,其余节点执行 leader 选举,新 leader 会向其余节点收回通告。通过该形式,实现集群中所有节点对全局拓扑图以及锁资源的路由算法达成统一。在成员变更期间,仍能够发动抢锁申请,但这些申请会在申请队列中,并不能抢锁胜利。成员变更完后,这些申请依照发动程序被从新收回。
  • 重建节点锁信息:leader 会告诉其余节点对锁信息进行重建,重建过程拆分为多个阶段,当所有节点实现一个阶段后,leader 会告诉集群所有节点进入下一个阶段。在重建过程中,任一节点产生故障,均须要从新发动选举和重建流程。重建分为以下阶段: 1) 节点清空目录信息 (锁的路由表) 以及节点持有的锁,这是因为锁资源信息须要从新路由;2) 对于之前节点持有的锁,依照原来的路由策略和程序从新发动加锁,这个过程会将整个集群的锁的目录信息从新建设起来,锁的 Master 从新确定。因为每个节点对仅对本身从新加锁,那么对于产生故障被删除节点而言,它之前持有的锁 Master 会被新节点代替;3) 所有节点实现从新加锁流程后,就能够执行失常的加解锁流程了。

从上述过程,咱们能够看到集群产生节点成员变更时,复原过程是非常复杂的。为了缩小这种状况的产生,当一个节点通信失败后,会期待肯定工夫,超过该距离后仍无奈失常通信,才会执行删除节点的流程。一个节点如果仅是产生重启,没有达到须要触发成员变更的阈值,那么只须要复原这个节点就能够了。在这个过程中,仅仅该节点的锁相干信息失落了,对于集群的其余节点没有影响。重启过程中,发往该节点的申请将会被 Pending 住,直到该节点复原。

产生重启的节点,下面大部分锁仍能复原。节点上的锁由两局部组成,一部分为 Local Lock,示意发动加锁的为节点本身。另一部分为 Remote Lock,示意由其余节点发动的加锁。对于 Local Lock,其余节点没有信息无奈复原,但不存在竞争,也无需复原;对于 RemoteLock,能够从其余节点的 shadow 信息中进行复原。

3.4 些许思考

从成员变更过程,咱们能够看到,Connection Manager 在 DLM 中承当了极其要害的角色,这个也是整个设计中最为简单的中央,当呈现节点故障时,由 Connection Manager 对立协调锁的重新分配,事实上承当了咱们所谓的分布式锁管控立体的工作。DLM 的长处是什么?负责分布式锁资源分配的数据立体不必思考整个零碎的容错,能够很平衡地让更多机器参加到资源分配,并且锁资源信息不须要落盘,不须要走共识协定做容错,只须要关注抢锁的互斥性和抢锁效率问题,这个抢锁效率,服务水平扩大能力都将十分有劣势。

通过上述对 DLM 的加锁,解锁及成员变更过程进行分析,这个外面还是有比拟清晰的管控立体与数据立体的解耦设计,当然,实现过程很简单,特地是 failover 这块复原逻辑。但这种思维还是十分好的,值得咱们做架构设计时候借鉴。尤其要提到一点,不同于 DLM 起源的 1980 年代,前期业界有了 Paxos/Raft/EPaxos 等共识协定,咱们也有了相似 ZooKeeper/Etcd 等基于共识协定的一致性协定,咱们的分布式锁管理器的管控立体齐全能够用起来这些成熟的三方组件。

四、最佳实际

阿里云存储部门领有着从块存储到文件存储,对象存储,日志存储,以及表格存储等寰球最残缺的存储产品体系。图 10 展现了以后存储产品采纳的十分通用的基于分区调度模型的零碎架构。整个业务零碎依照管控立体与数据立体来划分,其中数据立体将用户的存储空间依照肯定规定宰割成若干分区,在运行时一个分区会被调配至某个服务器提供服务,一个服务器能够同时加载多个分区。分区不应用本机文件系统存储长久化数据,其领有的全副数据均会存储在盘古分布式文件系统中的特定目录。基于如此的分区调度模型,当某个服务器产生宕机的时候,它承载的分区须要被从新调度,疾速迁徙至其它衰弱的服务器持续提供服务。

图 10 云存储基于盘古 + 女娲的通用的分区调度设计框架

在云存储的分区调度模型中,无关分区资源的互斥拜访(即任何时刻任一个分区必须至少为某一台服务器所加载并提供读写访问服务)是存储系统提供数据一致性的基石,必须失去保障。事实上,云存储的最佳实际中有着相似 DLM 的设计哲学,将分布式锁管理器的容错问题抽离进去,借助女娲 - 飞天分布式协同根底服务提供的选主性能来实现,进而能够专一在分布式锁资源的调度策略:

1)管控调度器负责具体分区资源的互斥调配,这里联合不同存储业务的非凡须要,能够演进出不同的调度策略,从分布式锁的均衡性,分布式锁的抢锁效率,分布式锁的切换精度等不同维度做专项优化;

2)分布式锁管理器中最简单的容错能力,通过依赖女娲选主性能实现,并且通过女娲的服务发现能力实现管控节点平滑高低线;

3)存储系统的数据最终均寄存在盘古 - 飞天分布式存储文件系统,从具体的分区数据,到管控调度的元数据,这些信息都会放入盘古。盘古提供了高牢靠高性能的存储服务以及 Fencing 爱护能力,保障数据一致性;

图 11 基于盘古分布式文件系统提供的 Fencing 爱护

分布式锁提供 Fencing 爱护的外围点是在访问共享资源的时候带上 Token 查看。盘古作为存储的对立的基座,通过引入非凡的 InlineFile 文件类型,配合 SealFile 操作,实现了相似的 IO Fence 爱护能力:a)SealFile 操作用来敞开曾经关上的文件,避免分布式锁旧的占有者持续写数据;b)为每个分区引入 InlineFile,针对盘古文件的元数据操作关联 InlineFile 相干的 CAS 判断,进而能够避免分布式锁旧的占有者关上新的文件。如图 11 所示,这两块性能联合起来事实上也是提供了存储系统中的写数据 Token 查看反对。

咱们看到,就云存储的 DLM 实现中,有一个通用的基于分区的调度器,有女娲提供容错保障,由盘古提供资源的 Fencing 爱护,这个就是云存储的最佳实际。

五、总结

分布式锁提供了分布式环境下共享资源的互斥拜访,在分布式系统中利用非常宽泛。这篇文章从分布式锁的性质登程,探讨了分布式锁的模型设计。就分布式锁零碎,咱们探讨了管制立体与数据立体解耦的架构设计,并介绍了阿里云存储场景下分布式锁的最佳实际。冀望咱们的分享对于读者敌人有所帮忙。

正文完
 0