五分钟学后端技术:分布式系统理论 - 从放弃到入门

转载声明javascript

本系列文章转自某技术大佬的博客https://www.cnblogs.com/bangerlee/html

该系列文章是我在网上可以找到的最全面的分布式理论介绍文章了,一直没看到有人整理这个系列文章,因此此次我就来作技术好文的搬运工,给整合了一把,以为写得好的朋友不妨去这位大佬的博客上打赏一把。java

分布式系统理论 - 从放弃到入门node

随承载用户数量的增长和容灾的须要,愈来愈多互联网后台系统从单机模式切换到分布式集群。回顾本身毕业五年来的工做内容,一样有这样的转变。程序员

毕业头两年负责维护运行在刀片机上的业务,在机房里拔插单板的日子是我逝去的青春。设备之间经过VCS组成冷备,但即便有双机软件保护,宕机、网络丢包等问题发生时业务仍会受影响。这样的系统架构下为保证SLA,有时候须要深刻Linux系统内核或硬件层面分析机器重启的缘由。web

接下来负责维护承载在分布式集群上的业务,相比前面的工做,这个阶段主要关注点不是单节点的异常,更可能是系统总体的稳定和健壮。面对纷繁复杂的系统,刚开始的时候有这样的感受:算法

庞大复杂的分布式系统前,应该从哪方面入手提高对其的认识和理解、提高专业性?网上能够找到不少分布式系统相关的论文和资料,但概括起来要表达的主要意思是什么?数据库

结合本身这几年的工做经验,总结分布式系统的核心就是解决一个问题:不一样节点间如何达成共识。promise

看似简单的问题因网络丢包、节点宕机恢复等场景变得复杂,由此才衍生出不少概念、协议和理论。为探究共识问题最大能解决的程度,因而有FLP、CAP边界理论;为在特定条件和范围内解决该问题,因而有一致性协议Paxos、Raft、Zab和Viewstamped Replication;为构建这些协议,因而有多数派、Leader选举、租约、逻辑时钟等概念和方法。缓存

2016年我阅读了分布式系统领域一些表明性的论文和博文,围绕“不一样节点如何达成共识”这个问题,加入本身的认识和理解后有下面7篇小结:

一致性、2PC和3PC
选举、多数派和租约
时间、时钟和事件顺序
CAP
Paxos
Raft、Zab
Paxos变种和优化
构思和写做技术类文章是一个辛苦的过程,一方面要阅读不少资料并转化成本身的理解、找到尽可能不拾人牙慧的立意和角度,一方面要绞尽脑汁组织语言让预期的读者可以容易理解。

但它也是一个有趣的过程,把知识捋一遍后本来一些模糊的概念变得清晰,写做过程当中想到的一些有意思的内容我也会将它穿插到文章里,有时候会被本身想到的一些小机灵逗乐 😃

但愿这几篇整理能为系统性地介绍分布式理论中文资料添一块砖、加一片瓦。

分布式系统理论基础 - 一致性、2PC和3PC

引言

狭义的分布式系统指由网络链接的计算机系统,每一个节点独立地承担计算或存储任务,节点间经过网络协同工做。广义的分布式系统是一个相对的概念,正如Leslie Lamport所说[1]:

What is a distributed systeme. Distribution is in the eye of the beholder. To the user sitting at the keyboard, his IBM personal computer is a nondistributed system. To a flea crawling around on the circuit board, or to the engineer who designed it, it's very much a distributed system.
一致性是分布式理论中的根本性问题,近半个世纪以来,科学家们围绕着一致性问题提出了不少理论模型,依据这些理论模型,业界也出现了不少工程实践投影。下面咱们从一致性问题、特定条件下解决一致性问题的两种方法(2PC、3PC)入门,了解最基础的分布式系统理论。

一致性(consensus)

何为一致性问题?简单而言,一致性问题就是相互独立的节点之间如何达成一项决议的问题。分布式系统中,进行数据库事务提交(commit transaction)、Leader选举、序列号生成等都会遇到一致性问题。这个问题在咱们的平常生活中也很常见,好比牌友怎么商定几点在哪打几圈麻将:

《赌圣》,1990

假设一个具备N个节点的分布式系统,当其知足如下条件时,咱们说这个系统知足一致性:

全认同(agreement): 全部N个节点都认同一个结果
值合法(validity): 该结果必须由N个节点中的节点提出
可结束(termination): 决议过程在必定时间内结束,不会无休止地进行下去
有人可能会说,决定何时在哪搓搓麻将,4我的商量一下就ok,这不很简单吗?

但就这样看似简单的事情,分布式系统实现起来并不轻松,由于它面临着这些问题:

消息传递异步无序(asynchronous): 现实网络不是一个可靠的信道,存在消息延时、丢失,节点间消息传递作不到同步有序(synchronous)
节点宕机(fail-stop): 节点持续宕机,不会恢复
节点宕机恢复(fail-recover): 节点宕机一段时间后恢复,在分布式系统中最多见
网络分化(network partition): 网络链路出现问题,将N个节点隔离成多个部分
拜占庭将军问题(byzantine failure)[2]: 节点或宕机或逻辑失败,甚至不按套路出牌抛出干扰决议的信息
假设现实场景中也存在这样的问题,咱们看看结果会怎样:

[复制代码](javascript:void(0); "复制代码")

我: 老王,今晚7点老地方,搓够48圈不见不散!
……
(次日凌晨3点) 隔壁老王: 没问题! // 消息延迟
我: ……

我: 小张,今晚7点老地方,搓够48圈不见不散!
小张: No ……
(两小时后……)
小张: No problem! // 宕机节点恢复
我: ……

我: 老李头,今晚7点老地方,搓够48圈不见不散!
老李: 必须的,大保健走起! // 拜占庭将军 (这是要打麻将呢?仍是要大保健?仍是一边打麻将一边大保健……)
还能不能一块儿愉快地玩耍...

咱们把以上所列的问题称为系统模型(system model),讨论分布式系统理论和工程实践的时候,必先划定模型。例若有如下两种模型:

异步环境(asynchronous)下,节点宕机(fail-stop)
异步环境(asynchronous)下,节点宕机恢复(fail-recover)、网络分化(network partition)
2比1多了节点恢复、网络分化的考量,于是对这两种模型的理论研究和工程解决方案一定是不一样的,在尚未明晰所要解决的问题前谈解决方案都是一本正经地耍流氓。

一致性还具有两个属性,一个是强一致(safety),它要求全部节点状态一致、共进退;一个是可用(liveness),它要求分布式系统24*7无间断对外服务。FLP定理(FLP impossibility)[3][4] 已经证实在一个收窄的模型中(异步环境并只存在节点宕机),不能同时知足 safety 和 liveness。

FLP定理是分布式系统理论中的基础理论,正如物理学中的能量守恒定律完全否认了永动机的存在,FLP定理否认了同时知足safety 和 liveness 的一致性协议的存在。

《怦然心动 (Flipped)》,2010

工程实践上根据具体的业务场景,或保证强一致(safety),或在节点宕机、网络分化的时候保证可用(liveness)。2PC、3PC是相对简单的解决一致性问题的协议,下面咱们就来了解2PC和3PC。

2PC

2PC(tow phase commit)两阶段提交[5]顾名思义它分红两个阶段,先由一方进行提议(propose)并收集其余节点的反馈(vote),再根据反馈决定提交(commit)或停止(abort)事务。咱们将提议的节点称为协调者(coordinator),其余参与决议节点称为参与者(participants, 或cohorts):

2PC, phase one

在阶段1中,coordinator发起一个提议,分别问询各participant是否接受。

2PC, phase two

在阶段2中,coordinator根据participant的反馈,提交或停止事务,若是participant所有赞成则提交,只要有一个participant不一样意就停止。

在异步环境(asynchronous)而且没有节点宕机(fail-stop)的模型下,2PC能够知足全认同、值合法、可结束,是解决一致性问题的一种协议。但若是再加上节点宕机(fail-recover)的考虑,2PC是否还能解决一致性问题呢?

coordinator若是在发起提议后宕机,那么participant将进入阻塞(block)状态、一直等待coordinator回应以完成该次决议。这时须要另外一角色把系统从不可结束的状态中带出来,咱们把新增的这一角色叫协调者备份(coordinator watchdog)。coordinator宕机必定时间后,watchdog接替原coordinator工做,经过问询(query) 各participant的状态,决定阶段2是提交仍是停止。这也要求 coordinator/participant 记录(logging)历史状态,以备coordinator宕机后watchdog对participant查询、coordinator宕机恢复后从新找回状态。

从coordinator接收到一次事务请求、发起提议到事务完成,通过2PC协议后增长了2次RTT(propose+commit),带来的时延(latency)增长相对较少。

3PC

3PC(three phase commit)即三阶段提交[6][7],既然2PC能够在异步网络+节点宕机恢复的模型下实现一致性,那还须要3PC作什么,3PC是什么鬼?

在2PC中一个participant的状态只有它本身和coordinator知晓,假如coordinator提议后自身宕机,在watchdog启用前一个participant又宕机,其余participant就会进入既不能回滚、又不能强制commit的阻塞状态,直到participant宕机恢复。这引出两个疑问:

能不能去掉阻塞,使系统能够在commit/abort前回滚(rollback)到决议发起前的初始状态
当次决议中,participant间能不能相互知道对方的状态,又或者participant间根本不依赖对方的状态
相比2PC,3PC增长了一个准备提交(prepare to commit)阶段来解决以上问题:

图片截取自wikipedia

coordinator接收完participant的反馈(vote)以后,进入阶段2,给各个participant发送准备提交(prepare to commit)指令。participant接到准备提交指令后能够锁资源,但要求相关操做必须可回滚。coordinator接收完确认(ACK)后进入阶段三、进行commit/abort,3PC的阶段3与2PC的阶段2无异。协调者备份(coordinator watchdog)、状态记录(logging)一样应用在3PC。

participant若是在不一样阶段宕机,咱们来看看3PC如何应对:

阶段1: coordinator或watchdog未收到宕机participant的vote,直接停止事务;宕机的participant恢复后,读取logging发现未发出同意vote,自行停止该次事务
阶段2: coordinator未收到宕机participant的precommit ACK,但由于以前已经收到了宕机participant的同意反馈(否则也不会进入到阶段2),coordinator进行commit;watchdog能够经过问询其余participant得到这些信息,过程同理;宕机的participant恢复后发现收到precommit或已经发出同意vote,则自行commit该次事务
阶段3: 即使coordinator或watchdog未收到宕机participant的commit ACK,也结束该次事务;宕机的participant恢复后发现收到commit或者precommit,也将自行commit该次事务
由于有了准备提交(prepare to commit)阶段,3PC的事务处理延时也增长了1个RTT,变为3个RTT(propose+precommit+commit),可是它防止participant宕机后整个系统进入阻塞态,加强了系统的可用性,对一些现实业务场景是很是值得的。

小结

以上介绍了分布式系统理论中的部分基础知识,阐述了一致性(consensus)的定义和实现一致性所要面临的问题,最后讨论在异步网络(asynchronous)、节点宕机恢复(fail-recover)模型下2PC、3PC怎么解决一致性问题。

阅读前人对分布式系统的各项理论研究,其中有严谨地推理、证实,有一种数学的美;观现实中的分布式系统实现,是综合各类因素下妥协的结果。

分布式系统理论基础 - 选举、多数派和租约

选举(election)是分布式系统实践中常见的问题,经过打破节点间的对等关系,选得的leader(或叫master、coordinator)有助于实现事务原子性、提高决议效率。 多数派(quorum)的思路帮助咱们在网络分化的状况下达成决议一致性,在leader选举的场景下帮助咱们选出惟一leader。租约(lease)在必定期限内给予节点特定权利,也能够用于实现leader选举。

下面咱们就来学习分布式系统理论中的选举、多数派和租约。

选举(electioin)

一致性问题(consistency)是独立的节点间如何达成决议的问题,选出你们都承认的leader本质上也是一致性问题,于是如何应对宕机恢复、网络分化等在leader选举中也须要考量。

Bully算法[1]是最多见的选举算法,其要求每一个节点对应一个序号,序号最高的节点为leader。leader宕机后次高序号的节点被重选为leader,过程以下:

(a). 节点4发现leader不可达,向序号比本身高的节点发起从新选举,从新选举消息中带上本身的序号

(b)(c). 节点五、6接收到重选信息后进行序号比较,发现自身的序号更大,向节点4返回OK消息并各自向更高序号节点发起从新选举

(d). 节点5收到节点6的OK消息,而节点6通过超时时间后收不到更高序号节点的OK消息,则认为本身是leader

(e). 节点6把本身成为leader的信息广播到全部节点

回顾《分布式系统理论基础 - 一致性、2PC和3PC》就能够看到,Bully算法中有2PC的身影,都具备提议(propose)和收集反馈(vote)的过程。

在一致性算法Paxos、ZAB[2]、Raft[3]中,为提高决议效率均有节点充当leader的角色。ZAB、Raft中描述了具体的leader选举实现,与Bully算法相似ZAB中使用zxid标识节点,具备最大zxid的节点表示其所具有的事务(transaction)最新、被选为leader。

多数派(quorum)

在网络分化的场景下以上Bully算法会遇到一个问题,被分隔的节点都认为本身具备最大的序号、将产生多个leader,这时候就须要引入多数派(quorum)[4]。多数派的思路在分布式系统中很常见,其确保网络分化状况下决议惟一。

多数派的原理提及来很简单,假如节点总数为2f+1,则一项决议获得多于 f 节点同意则得到经过。leader选举中,网络分化场景下只有具有多数派节点的部分才可能选出leader,这避免了多leader的产生。

多数派的思路还被应用于副本(replica)管理,根据业务实际读写比例调整写副本数Vw、读副本数Vr,用以在可靠性和性能方面取得平衡[5]。

租约(lease)

选举中很重要的一个问题,以上还没有提到:怎么判断leader不可用、何时应该发起从新选举?最早可能想到会经过心跳(heart beat)判别leader状态是否正常,但在网络拥塞或瞬断的状况下,这容易致使出现双主。

租约(lease)是解决该问题的经常使用方法,其最初提出时用于解决分布式缓存一致性问题[6],后面在分布式锁[7]等不少方面都有应用。

租约的原理一样不复杂,中心思想是每次租约时长内只有一个节点得到租约、到期后必须从新颁发租约。假设咱们有租约颁发节点Z,节点0、1和2竞选leader,租约过程以下:

(a). 节点0、一、2在Z上注册本身,Z根据必定的规则(例如先到先得)颁发租约给节点,该租约同时对应一个有效时长;这里假设节点0得到租约、成为leader

(b). leader宕机时,只有租约到期(timeout)后才从新发起选举,这里节点1得到租约、成为leader

租约机制确保了一个时刻最多只有一个leader,避免只使用心跳机制产生双主的问题。在实践应用中,zookeeper、ectd可用于租约颁发。

小结

在分布式系统理论和实践中,常见leader、quorum和lease的身影。分布式系统内不必定事事协商、事事民主,leader的存在有助于提高决议效率。

本文以leader选举做为例子引入和讲述quorum、lease,固然quorum和lease是两种思想,并不限于leader选举应用。

最后提一个有趣的问题与你们思考,leader选举的本质是一致性问题,Paxos、Raft和ZAB等解决一致性问题的协议和算法自己又须要或依赖于leader,怎么理解这个看似“蛋生鸡、鸡生蛋”的问题?[8]

分布式系统理论基础 - 时间、时钟和事件顺序

十六号…… 四月十六号。一九六零年四月十六号下午三点以前的一分钟你和我在一块儿,由于你我会记住这一分钟。从如今开始咱们就是一分钟的朋友,这是事实,你改变不了,由于已通过去了。我明天会再来。

—— 《阿飞正传》
现实生活中时间是很重要的概念,时间能够记录事情发生的时刻、比较事情发生的前后顺序。分布式系统的一些场景也须要记录和比较不一样节点间事件发生的顺序,但不一样于平常生活使用物理时钟记录时间,分布式系统使用逻辑时钟记录事件顺序关系,下面咱们来看分布式系统中几种常见的逻辑时钟。

物理时钟 vs 逻辑时钟

可能有人会问,为何分布式系统不使用物理时钟(physical clock)记录事件?每一个事件对应打上一个时间戳,当须要比较顺序的时候比较相应时间戳就行了。

这是由于现实生活中物理时间有统一的标准,而分布式系统中每一个节点记录的时间并不同,即便设置了 NTP 时间同步节点间也存在毫秒级别的误差[1][2]。于是分布式系统须要有另外的方法记录事件顺序关系,这就是逻辑时钟(logical clock)。

Lamport timestamps

Leslie Lamport 在1978年提出逻辑时钟的概念,并描述了一种逻辑时钟的表示方法,这个方法被称为Lamport时间戳(Lamport timestamps)[3]。

分布式系统中按是否存在节点交互可分为三类事件,一类发生于节点内部,二是发送事件,三是接收事件。Lamport时间戳原理以下:

图1: Lamport timestamps space time (图片来源: wikipedia)

每一个事件对应一个Lamport时间戳,初始值为0
若是事件在节点内发生,时间戳加1
若是事件属于发送事件,时间戳加1并在消息中带上该时间戳
若是事件属于接收事件,时间戳 = Max(本地时间戳,消息中的时间戳) + 1
假设有事件a、b,C(a)、C(b)分别表示事件a、b对应的Lamport时间戳,若是C(a) < C(b),则有a发生在b以前(happened before),记做 a -> b,例如图1中有 C1 -> B1。经过该定义,事件集中Lamport时间戳不等的事件可进行比较,咱们得到事件的偏序关系(partial order)。

若是C(a) = C(b),那a、b事件的顺序又是怎样的?假设a、b分别在节点P、Q上发生,Pi、Qj分别表示咱们给P、Q的编号,若是 C(a) = C(b) 而且 Pi <Qj,一样定义为a发生在b以前,记做 a => b。假如咱们对图1的A、B、C分别编号Ai = 一、Bj = 二、Ck = 3,因 C(B4) = C(C3) 而且 Bj < Ck,则 B4 => C3。

经过以上定义,咱们能够对全部事件排序、得到事件的全序关系(total order)。上图例子,咱们能够从C1到A4进行排序。

Vector clock

Lamport时间戳帮助咱们获得事件顺序关系,但还有一种顺序关系不能用Lamport时间戳很好地表示出来,那就是同时发生关系(concurrent)[4]。例如图1中事件B4和事件C3没有因果关系,属于同时发生事件,但Lamport时间戳定义二者有前后顺序。

Vector clock是在Lamport时间戳基础上演进的另外一种逻辑时钟方法,它经过vector结构不但记录本节点的Lamport时间戳,同时也记录了其余节点的Lamport时间戳[5][6]。Vector clock的原理与Lamport时间戳相似,使用图例以下:

图2: Vector clock space time (图片来源: wikipedia)

假设有事件a、b分别在节点P、Q上发生,Vector clock分别为Ta、Tb,若是 Tb[Q] > Ta[Q] 而且 Tb[P] >= Ta[P],则a发生于b以前,记做 a -> b。到目前为止还和Lamport时间戳差异不大,那Vector clock怎么判别同时发生关系呢?

若是 Tb[Q] > Ta[Q] 而且 Tb[P] < Ta[P],则认为a、b同时发生,记做 a <-> b。例如图2中节点B上的第4个事件 (A:2,B:4,C:1) 与节点C上的第2个事件 (B:3,C:2) 没有因果关系、属于同时发生事件。

Version vector

基于Vector clock咱们能够得到任意两个事件的顺序关系,结果或为前后顺序或为同时发生,识别事件顺序在工程实践中有很重要的引伸应用,最多见的应用是发现数据冲突(detect conflict)。

分布式系统中数据通常存在多个副本(replication),多个副本可能被同时更新,这会引发副本间数据不一致[7],Version vector的实现与Vector clock很是相似[8],目的用于发现数据冲突[9]。下面经过一个例子说明Version vector的用法[10]:

图3: Version vector

client端写入数据,该请求被Sx处理并建立相应的vector ([Sx, 1]),记为数据D1
第2次请求也被Sx处理,数据修改成D2,vector修改成([Sx, 2])
第三、第4次请求分别被Sy、Sz处理,client端先读取到D2,而后D三、D4被写入Sy、Sz
第5次更新时client端读取到D二、D3和D4 3个数据版本,经过相似Vector clock判断同时发生关系的方法可判断D三、D4存在数据冲突,最终经过必定方法解决数据冲突并写入D5
Vector clock只用于发现数据冲突,不能解决数据冲突。如何解决数据冲突因场景而异,具体方法有以最后更新为准(last write win),或将冲突的数据交给client由client端决定如何处理,或经过quorum决议事先避免数据冲突的状况发生[11]。

因为记录了全部数据在全部节点上的逻辑时钟信息,Vector clock和Version vector在实际应用中可能面临的一个问题是vector过大,用于数据管理的元数据(meta data)甚至大于数据自己[12]。

解决该问题的方法是使用server id取代client id建立vector (由于server的数量相对client稳定),或设定最大的size、若是超过该size值则淘汰最旧的vector信息[10][13]。

小结

以上介绍了分布式系统里逻辑时钟的表示方法,经过Lamport timestamps能够创建事件的全序关系,经过Vector clock能够比较任意两个事件的顺序关系而且能表示无因果关系的事件,将Vector clock的方法用于发现数据版本冲突,因而有了Version vector。

分布式系统理论基础 - CAP

引言

CAP是分布式系统、特别是分布式存储领域中被讨论最多的理论,“什么是CAP定理?”在Quora 分布式系统分类下排名 FAQ 的 No.1。CAP在程序员中也有较广的普及,它不只仅是“C、A、P不能同时知足,最多只能3选2”,如下尝试综合各方观点,从发展历史、工程实践等角度讲述CAP理论。但愿你们透过本文对CAP理论有更多地了解和认识。

CAP定理

CAP由Eric Brewer在2000年PODC会议上提出[1][2],是Eric Brewer在Inktomi[3]期间研发搜索引擎、分布式web缓存时得出的关于数据一致性(consistency)、服务可用性(availability)、分区容错性(partition-tolerance)的猜测:

It is impossible for a web service to provide the three following guarantees : Consistency, Availability and Partition-tolerance.
该猜测在提出两年后被证实成立[4],成为咱们熟知的CAP定理:

数据一致性(consistency):若是系统对一个写操做返回成功,那么以后的读请求都必须读到这个新数据;若是返回失败,那么全部读操做都不能读到这个数据,对调用者而言数据具备强一致性(strong consistency) (又叫原子性 atomic、线性一致性 linearizable consistency)[5]
服务可用性(availability):全部读写请求在必定时间内获得响应,可终止、不会一直等待
分区容错性(partition-tolerance):在网络分区的状况下,被分隔的节点仍能正常对外服务
在某时刻若是知足AP,分隔的节点同时对外服务但不能相互通讯,将致使状态不一致,即不能知足C;若是知足CP,网络分区的状况下为达成C,请求只能一直等待,即不知足A;若是要知足CA,在必定时间内要达到节点状态一致,要求不能出现网络分区,则不能知足P。

C、A、P三者最多只能知足其中两个,和FLP定理同样,CAP定理也指示了一个不可达的结果(impossibility result)。

CAP的工程启示

CAP理论提出七、8年后,NoSql圈将CAP理论看成对抗传统关系型数据库的依据、阐明本身放宽对数据一致性(consistency)要求的正确性[6],随后引发了大范围关于CAP理论的讨论。

CAP理论看似给咱们出了一道3选2的选择题,但在工程实践中存在不少现实限制条件,须要咱们作更多地考量与权衡,避免进入CAP认识误区[7]。

一、关于 P 的理解

Partition字面意思是网络分区,即因网络因素将系统分隔为多个单独的部分,有人可能会说,网络分区的状况发生几率很是小啊,是否是不用考虑P,保证CA就好[8]。要理解P,咱们看回CAP证实[4]中P的定义:

In order to model partition tolerance, the network will be allowed to lose arbitrarily many messages sent from one node to another.
网络分区的状况符合该定义,网络丢包的状况也符合以上定义,另外节点宕机,其余节点发往宕机节点的包也将丢失,这种状况一样符合定义。现实状况下咱们面对的是一个不可靠的网络、有必定几率宕机的设备,这两个因素都会致使Partition,于是分布式系统实现中 P 是一个必须项,而不是可选项[9][10]。

对于分布式系统工程实践,CAP理论更合适的描述是:在知足分区容错的前提下,没有算法能同时知足数据一致性和服务可用性[11]:

In a network subject to communication failures, it is impossible for any web service to implement an atomic read/write shared memory that guarantees a response to every request.
二、CA非0/1的选择

P 是必选项,那3选2的选择题不就变成数据一致性(consistency)、服务可用性(availability) 2选1?工程实践中一致性有不一样程度,可用性也有不一样等级,在保证分区容错性的前提下,放宽约束后能够兼顾一致性和可用性,二者不是非此即彼[12]。

CAP定理证实中的一致性指强一致性,强一致性要求多节点组成的被调要能像单节点同样运做、操做具有原子性,数据在时间、时序上都有要求。若是放宽这些要求,还有其余一致性类型:

序列一致性(sequential consistency)[13]:不要求时序一致,A操做先于B操做,在B操做后若是全部调用端读操做获得A操做的结果,知足序列一致性
最终一致性(eventual consistency)[14]:放宽对时间的要求,在被调完成操做响应后的某个时间点,被调多个节点的数据最终达成一致
可用性在CAP定理里指全部读写操做必需要能终止,实际应用中从主调、被调两个不一样的视角,可用性具备不一样的含义。当P(网络分区)出现时,主调能够只支持读操做,经过牺牲部分可用性达成数据一致。

工程实践中,较常见的作法是经过异步拷贝副本(asynchronous replication)、quorum/NRW,实如今调用端看来数据强一致、被调端最终一致,在调用端看来服务可用、被调端容许部分节点不可用(或被网络分隔)的效果[15]。

三、跳出CAP

CAP理论对实现分布式系统具备指导意义,但CAP理论并无涵盖分布式工程实践中的全部重要因素。

例如延时(latency),它是衡量系统可用性、与用户体验直接相关的一项重要指标[16]。CAP理论中的可用性要求操做能终止、不无休止地进行,除此以外,咱们还关心到底须要多长时间能结束操做,这就是延时,它值得咱们设计、实现分布式系统时单列出来考虑。

延时与数据一致性也是一对“冤家”,若是要达到强一致性、多个副本数据一致,必然增长延时。加上延时的考量,咱们获得一个CAP理论的修改版本PACELC[17]:若是出现P(网络分区),如何在A(服务可用性)、C(数据一致性)之间选择;不然,如何在L(延时)、C(数据一致性)之间选择。

小结

以上介绍了CAP理论的源起和发展,介绍了CAP理论给分布式系统工程实践带来的启示。

CAP理论对分布式系统实现有很是重大的影响,咱们能够根据自身的业务特色,在数据一致性和服务可用性之间做出倾向性地选择。经过放松约束条件,咱们能够实如今不一样时间点知足CAP(此CAP非CAP定理中的CAP,如C替换为最终一致性)[18][19][20]。

有很是很是多文章讨论和研究CAP理论,但愿这篇对你认识和了解CAP理论有帮助。

分布式系统理论进阶 - Paxos

引言

《分布式系统理论基础 - 一致性、2PC和3PC》一文介绍了一致性、达成一致性须要面临的各类问题以及2PC、3PC模型,Paxos协议在节点宕机恢复、消息无序或丢失、网络分化的场景下能保证决议的一致性,是被讨论最普遍的一致性协议。

Paxos协议同时又以其“艰深晦涩”著称,下面结合 Paxos Made Simple、The Part-Time Parliament 两篇论文,尝试经过Paxos推演、学习和了解Paxos协议。

Basic Paxos

何为一致性问题?简单而言,一致性问题是在节点宕机、消息无序等场景可能出现的状况下,相互独立的节点之间如何达成决议的问题,做为解决一致性问题的协议,Paxos的核心是节点间如何肯定并只肯定一个值(value)。

也许你会疑惑只肯定一个值能起什么做用,在Paxos协议里肯定并只肯定一个值是肯定多值的基础,如何肯定多值将在第二部分Multi Paxos中介绍,这部分咱们聚焦在“Paxos如何肯定并只肯定一个值”这一问题上。

和2PC相似,Paxos先把节点分红两类,发起提议(proposal)的一方为proposer,参与决议的一方为acceptor。假如只有一个proposer发起提议,而且节点不宕机、消息不丢包,那么acceptor作到如下这点就能够肯定一个值:

P1. 一个acceptor接受它收到的第一项提议
固然上面要求的前提条件有些严苛,节点不能宕机、消息不能丢包,还只能由一个proposer发起提议。咱们尝试放宽条件,假设多个proposer能够同时发起提议,又怎样才能作到肯定并只肯定一个值呢?

首先proposer和acceptor须要知足如下两个条件:

  1. proposer发起的每项提议分别用一个ID标识,提议的组成所以变为(ID, value)

  2. acceptor能够接受(accept)不止一项提议,当多数(quorum) acceptor接受一项提议时该提议被肯定(chosen)

(注: 注意以上“接受”和“肯定”的区别)

咱们约定后面发起的提议的ID比前面提议的ID大,并假设能够有多项提议被肯定,为作到肯定并只肯定一个值acceptor要作到如下这点:

P2. 若是一项值为v的提议被肯定,那么后续只肯定值为v的提议
(注: 乍看这个条件不太好理解,谨记目标是“肯定并只肯定一个值”)

因为一项提议被肯定(chosen)前必须先被多数派acceptor接受(accepted),为实现P2,实质上acceptor须要作到:

P2a. 若是一项值为v的提议被肯定,那么acceptor后续只接受值为v的提议
知足P2a则P2成立 (P2a => P2)。

目前在多个proposer能够同时发起提议的状况下,知足P一、P2a即能作到肯定并只肯定一个值。若是再加上节点宕机恢复、消息丢包的考量呢?

假设acceptor c 宕机一段时间后恢复,c 宕机期间其余acceptor已经肯定了一项值为v的决议但c 由于宕机并不知晓;c 恢复后若是有proposer立刻发起一项值不是v的提议,因为条件P1,c 会接受该提议,这与P2a矛盾。为了不这样的状况出现,进一步地咱们对proposer做约束:

P2b. 若是一项值为v的提议被肯定,那么proposer后续只发起值为v的提议
知足P2b则P2a成立 (P2b => P2a => P2)。

P2b约束的是提议被肯定(chosen)后proposer的行为,咱们更关心提议被肯定前proposer应该怎么作:

P2c. 对于提议(n,v),acceptor的多数派S中,若是存在acceptor最近一次(即ID值最大)接受的提议的值为v',那么要求v = v';不然v可为任意值
知足P2c则P2b成立 (P2c => P2b => P2a => P2)。

条件P2c是Basic Paxos的核心,光看P2c的描述可能会以为一头雾水,咱们经过 The Part-Time Parliament 中的例子加深理解:

假设有A~E 5个acceptor,- 表示acceptor因宕机等缘由缺席当次决议,x 表示acceptor不接受提议,o 表示接受提议;多数派acceptor接受提议后提议被肯定,以上表格对应的决议过程以下:

ID为2的提议最先提出,根据P2c其提议值可为任意值,这里假设为a
acceptor A/B/C/E 在以前的决议中没有接受(accept)任何提议,于是ID为5的提议的值也能够为任意值,这里假设为b
acceptor B/D/E,其中D曾接受ID为2的提议,根据P2c,该轮ID为14的提议的值必须与ID为2的提议的值相同,为a
acceptor A/C/D,其中D曾接受ID为2的提议、C曾接受ID为5的提议,相比之下ID 5较ID 2大,根据P2c,该轮ID为27的提议的值必须与ID为5的提议的值相同,为b;该轮决议被多数派acceptor接受,所以该轮决议得以肯定
acceptor B/C/D,3个acceptor以前都接受过提议,相比之下C、D曾接受的ID 27的ID号最大,该轮ID为29的提议的值必须与ID为27的提议的值相同,为b
以上提到的各项约束条件能够概括为3点,若是proposer/acceptor知足下面3点,那么在少数节点宕机、网络分化隔离的状况下,在“肯定并只肯定一个值”这件事情上能够保证一致性(consistency):

B1(ß): ß中每一轮决议都有惟一的ID标识
B2(ß): 若是决议B被acceptor多数派接受,则肯定决议B
B3(ß): 对于ß中的任意提议B(n,v),acceptor的多数派中若是存在acceptor最近一次(即ID值最大)接受的提议的值为v',那么要求v = v';不然v可为任意值
(注: 希腊字母ß表示多轮决议的集合,字母B表示一轮决议)

另外为保证P2c,咱们对acceptor做两个要求:

  1. 记录曾接受的ID最大的提议,因proposer须要问询该信息以决定提议值

  2. 在回应提议ID为n的proposer本身曾接受过ID最大的提议时,acceptor同时保证(promise)再也不接受ID小于n的提议

至此,proposer/acceptor完成一轮决议可概括为prepare和accept两个阶段。prepare阶段proposer发起提议问询提议值、acceptor回应问询并进行promise;accept阶段完成决议,图示以下:

还有一个问题须要考量,假如proposer A发起ID为n的提议,在提议未完成前proposer B又发起ID为n+1的提议,在n+1提议未完成前proposer C又发起ID为n+2的提议…… 如此acceptor不能完成决议、造成活锁(livelock),虽然这不影响一致性,但咱们通常不想让这样的状况发生。解决的方法是从proposer中选出一个leader,提议统一由leader发起。

最后咱们再引入一个新的角色:learner,learner依附于acceptor,用于习得已肯定的决议。以上决议过程都只要求acceptor多数派参与,而咱们但愿尽可能全部acceptor的状态一致。若是部分acceptor因宕机等缘由未知晓已肯定决议,宕机恢复后可经本机learner采用pull的方式从其余acceptor习得。

Multi Paxos

经过以上步骤分布式系统已经能肯定一个值,“只肯定一个值有什么用?这可解决不了我面临的问题。” 你心中可能有这样的疑问。

其实不断地进行“肯定一个值”的过程、再为每一个过程编上序号,就能获得具备全序关系(total order)的系列值,进而能应用在数据库副本存储等不少场景。咱们把单次“肯定一个值”的过程称为实例(instance),它由proposer/acceptor/learner组成,下图说明了A/B/C三机上的实例:

不一样序号的实例之间互相不影响,A/B/C三机输入相同、过程实质等同于执行相同序列的状态机(state machine)指令 ,于是将获得一致的结果。

proposer leader在Multi Paxos中还有助于提高性能,常态下统一由leader发起提议,可节省prepare步骤(leader不用问询acceptor曾接受过的ID最大的提议、只有leader提议也不须要acceptor进行promise)直至发生leader宕机、从新选主。

小结

以上介绍了Paxos的推演过程、如何在Basic Paxos的基础上经过状态机构建Multi Paxos。Paxos协议比较“艰深晦涩”,但多读几遍论文通常能理解其内涵,更难的是如何将Paxos真正应用到工程实践。

微信后台开发同窗实现并开源了一套基于Paxos协议的多机状态拷贝类库PhxPaxos,PhxPaxos用于将单机服务扩展到多机,其通过线上系统验证并在一致性保证、性能等方面做了不少考量。

分布式系统理论进阶 - Raft、Zab

引言

《分布式系统理论进阶 - Paxos》介绍了一致性协议Paxos,今天咱们来学习另外两个常见的一致性协议——Raft和Zab。经过与Paxos对比,了解Raft和Zab的核心思想、加深对一致性协议的认识。

Raft

Paxos偏向于理论、对如何应用到工程实践说起较少。理解的难度加上现实的骨感,在生产环境中基于Paxos实现一个正确的分布式系统很是难[1]:

There are significant gaps between the description of the Paxos algorithm and the needs of a real-world system. In order to build a real-world system, an expert needs to use numerous ideas scattered in the literature and make several relatively small protocol extensions. The cumulative effort will be substantial and the final system will be based on an unproven protocol.

Raft[2][3]在2013年提出,提出的时间虽然不长,但已经有不少系统基于Raft实现。相比Paxos,Raft的买点就是更利于理解、更易于实行。

为达到更容易理解和实行的目的,Raft将问题分解和具体化:Leader统一处理变动操做请求,一致性协议的做用具化为保证节点间操做日志副本(log replication)一致,以term做为逻辑时钟(logical clock)保证时序,节点运行相同状态机(state machine)[4]获得一致结果。Raft协议具体过程以下:

Client发起请求,每一条请求包含操做指令
请求交由Leader处理,Leader将操做指令(entry)追加(append)至操做日志,紧接着对Follower发起AppendEntries请求、尝试让操做日志副本在Follower落地
若是Follower多数派(quorum)赞成AppendEntries请求,Leader进行commit操做、把指令交由状态机处理
状态机处理完成后将结果返回给Client
指令经过log index(指令id)和term number保证时序,正常状况下Leader、Follower状态机按相同顺序执行指令,得出相同结果、状态一致。

宕机、网络分化等状况可引发Leader从新选举(每次选举产生新Leader的同时,产生新的term)、Leader/Follower间状态不一致。Raft中Leader为本身和全部Follower各维护一个nextIndex值,其表示Leader紧接下来要处理的指令id以及将要发给Follower的指令id,LnextIndex不等于FnextIndex时表明Leader操做日志和Follower操做日志存在不一致,这时将从Follower操做日志中最初不一致的地方开始,由Leader操做日志覆盖Follower,直到LnextIndex、FnextIndex相等。

Paxos中Leader的存在是为了提高决议效率,Leader的有无和数目并不影响决议一致性,Raft要求具有惟一Leader,并把一致性问题具体化为保持日志副本的一致性,以此实现相较Paxos而言更容易理解、更容易实现的目标。

Zab

Zab[5][6]的全称是Zookeeper atomic broadcast protocol,是Zookeeper内部用到的一致性协议。相比Paxos,Zab最大的特色是保证强一致性(strong consistency,或叫线性一致性linearizable consistency)。

和Raft同样,Zab要求惟一Leader参与决议,Zab能够分解成discovery、sync、broadcast三个阶段:

discovery: 选举产生PL(prospective leader),PL收集Follower epoch(cepoch),根据Follower的反馈PL产生newepoch(每次选举产生新Leader的同时产生新epoch,相似Raft的term)
sync: PL补齐相比Follower多数派缺失的状态、以后各Follower再补齐相比PL缺失的状态,PL和Follower完成状态同步后PL变为正式Leader(established leader)
broadcast: Leader处理Client的写操做,并将状态变动广播至Follower,Follower多数派经过以后Leader发起将状态变动落地(deliver/commit)
Leader和Follower之间经过心跳判别健康状态,正常状况下Zab处在broadcast阶段,出现Leader宕机、网络隔离等异常状况时Zab从新回到discovery阶段。

了解完Zab的基本原理,咱们再来看Zab怎样保证强一致性,Zab经过约束事务前后顺序达到强一致性,先广播的事务先commit、FIFO,Zab称之为primary order(如下简称PO)。实现PO的核心是zxid。

Zab中每一个事务对应一个zxid,它由两部分组成:<e, c>,e即Leader选举时生成的epoch,c表示当次epoch内事务的编号、依次递增。假设有两个事务的zxid分别是z、z',当知足 z.e < z'.e 或者 z.e = z'.e && z.c < z'.c 时,定义z先于z'发生(z < z')。

为实现PO,Zab对Follower、Leader有如下约束:

有事务z和z',若是Leader先广播z,则Follower需保证先commit z对应的事务
有事务z和z',z由Leader p广播,z'由Leader q广播,Leader p先于Leader q,则Follower需保证先commit z对应的事务
有事务z和z',z由Leader p广播,z'由Leader q广播,Leader p先于Leader q,若是Follower已经commit z,则q需保证已commit z才能广播z'
第一、2点保证事务FIFO,第3点保证Leader上具有全部已commit的事务。

相比Paxos,Zab约束了事务顺序、适用于有强一致性需求的场景。

Paxos、Raft、Zab再比较

除Paxos、Raft和Zab外,Viewstamped Replication(简称VR)[7][8]也是讨论比较多的一致性协议。这些协议包含不少共同的内容(Leader、quorum、state machine等),于是咱们不由要问:Paxos、Raft、Zab和VR等分布式一致性协议区别到底在哪,仍是根本就是一回事?[9]

Paxos、Raft、Zab和VR都是解决一致性问题的协议,Paxos协议原文倾向于理论,Raft、Zab、VR倾向于实践,一致性保证程度等的不一样也致使这些协议间存在差别。下图帮助咱们理解这些协议的类似点和区别[10]:

相比Raft、Zab、VR,Paxos更纯粹、更接近一致性问题本源,尽管Paxos倾向理论,但不表明Paxos不能应用于工程。基于Paxos的工程实践,须考虑具体需求场景(如一致性要达到什么程度),再在Paxos原始语意上进行包装。

小结

以上介绍分布式一致性协议Raft、Zab的核心思想,分析Raft、Zab与Paxos的异同。实现分布式系统时,先从具体需求和场景考虑,Raft、Zab、VR、Paxos等协议没有绝对地好与很差,只是适不适合。

分布式系统理论进阶 - Paxos变种和优化

引言

《分布式系统理论进阶 - Paxos》中咱们了解了Basic Paxos、Multi Paxos的基本原理,但若是想把Paxos应用于工程实践,了解基本原理还不够。

有不少基于Paxos的优化,在保证一致性协议正确(safety)的前提下,减小Paxos决议通讯步骤、避免单点故障、实现节点负载均衡,从而下降时延、增长吞吐量、提高可用性,下面咱们就来了解这些Paxos变种。

Multi Paxos

首先咱们来回顾一下Multi Paxos,Multi Paxos在Basic Paxos的基础上肯定一系列值,其决议过程以下:

phase1a: leader提交提议给acceptor

phase1b: acceptor返回最近一次接受的提议(即曾接受的最大的提议ID和对应的value),未接受过提议则返回空

phase2a: leader收集acceptor的应答,分两种状况处理

phase2a.1: 若是应答内容都为空,则自由选择一个提议value

phase2a.2: 若是应答内容不为空,则选择应答里面ID最大的提议的value

phase2b: acceptor将决议同步给learner

Multi Paxos中leader用于避免活锁,但leader的存在会带来其余问题,一是如何选举和保持惟一leader(虽然无leader或多leader不影响一致性,但影响决议进程progress),二是充当leader的节点会承担更多压力,如何均衡节点的负载。Mencius[1]提出节点轮流担任leader,以达到均衡负载的目的;租约(lease)能够帮助实现惟一leader,但leader故障状况下可致使服务短时间不可用。

Fast Paxos

在Multi Paxos中,proposer -> leader -> acceptor -> learner,从提议到完成决议共通过3次通讯,能不能减小通讯步骤?

对Multi Paxos phase2a,若是能够自由提议value,则可让proposer直接发起提议、leader退出通讯过程,变为proposer -> acceptor -> learner,这就是Fast Paxos[2]的由来。

Multi Paxos里提议都由leader提出,于是不存在一次决议出现多个value,Fast Paxos里由proposer直接提议,一次决议里可能有多个proposer提议、出现多个value,即出现提议冲突(collision)。leader起到初始化决议进程(progress)和解决冲突的做用,当冲突发生时leader从新参与决议过程、回退到3次通讯步骤。

Paxos自身隐含的一个特性也能够达到减小通讯步骤的目标,若是acceptor上一次肯定(chosen)的提议来自proposerA,则当次决议proposerA能够直接提议减小一次通讯步骤。若是想实现这样的效果,须要在proposer、acceptor记录上一次决议肯定(chosen)的历史,用以在提议前知道哪一个proposer的提议上一次被肯定、当次决议能不能节省一次通讯步骤。

EPaxos

除了从减小通讯步骤的角度提升Paxos决议效率外,还有其余方面能够下降Paxos决议时延,好比Generalized Paxos[3]提出不冲突的提议(例如对不一样key的写请求)能够同时决议、以下降Paxos时延。

更进一步地,EPaxos[4](Egalitarian Paxos)提出一种既支持不冲突提议同时提交下降时延、还均衡各节点负载、同时将通讯步骤减小到最少的Paxos优化方法。

为达到这些目标,EPaxos的实现有几个要点。一是EPaxos中没有全局的leader,而是每一次提议发起提议的proposer做为当次提议的leader(command leader);二是不相互影响(interfere)的提议能够同时提交;三是跳过prepare,直接进入accept阶段。EPaxos决议的过程以下:

左侧展现了互不影响的两个update请求的决议过程,右侧展现了相互影响的两个update请求的决议。Multi Paxos、Mencius、EPaxos时延和吞吐量对比:

为判断决议是否相互影响,实现EPaxos得记录决议之间的依赖关系。

小结

以上介绍了几个基于Paxos的变种,Mencius中节点轮流作leader、均衡节点负载,Fast Paxos减小一次通讯步骤,Generalized Paxos容许互不影响的决议同时进行,EPaxos无全局leader、各节点平等分担负载。

优化无止境,对Paxos也同样,应用在不一样场景和不一样范围的Paxos变种和优化将继续不断出现。

相关文章
相关标签/搜索