常用的分布式一致性协议

分布式一致性

在分布式系统中,为了保证数据的高可用,通常,我们会将数据保留多个副本(replica),这些副本会放置在不同的物理的机器上。为了对用户提供正确的增\删\改\差等语义,我们需要保证这些放置在不同物理机器上的副本是一致的。分布式一致性又分为强一致性和弱一致性:

弱一致性:

  • 最终一致性:DNS、Gossip协议等

强一致性:

  • 同步
  • Paxos
  • Raft
  • ZAB

Base理论

BASE全称:Basically Available(基本可用),Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写,来自 ebay 的架构师提出。

Base 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:

虽然无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

1.基本可用

基本可用是指:系统出现了不可预知的故障,但还是能用,相比较正常的系统而言:

  • 响应时间上的损失:正常情况下的搜索引擎 0.5 秒即返回给用户结果,而基本可用的搜索引擎可以在 1 秒作用返回结果。
  • 功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单,但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。

2.软状态

软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。例如MySQL读写分离的延时。

3.最终一致性

上面说软状态,然后不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。

2PC

为了解决这种分布式一致性问题,前人在性能和数据一致性的反反复复权衡过程中总结了许多典型的协议和算法。其中比较著名的有二阶段提交协议(Two Phase Commitment Protocol)、三阶段提交协议(Three Phase Commitment Protocol)和Paxos算法

当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

图片名称

3PC

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态(但是这种机制也会导致数据一致性问题)。

3PC 将阶段一 “提交事务请求” 分成了2部分,总共形成了3个部分:

  1. Can Commit(非阻塞)
  2. Pre Commit
  3. do Commit

无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。Google Chubby的作者Mike Burrows说过there is only one consensus protocol, and that’s Paxos” – all other approaches are just broken versions of Paxos。意即世上只有一种一致性算法,那就是Paxos,所有其他一致性算法都是Paxos算法的不完整版。

Paxos算法

复制状态机

分布式系统对fault tolorence(容错)的一般解决方案是state machine replication。

state machine replication:复制状态机

一个分布式的复制状态机系统由多个复制单元组成,每个复制单元均是一个状态机,它的状态保存在一组状态变量中,状态机的变量只能通过外部命令来改变。简单理解的话,可以想象成是一组服务器,每个服务器是一个状态机,服务器的运行状态只能通过一行行的命令来改变。每一个状态机存储一个包含一系列指令的日志,严格按照顺序逐条执行日志中的指令,如果所有的状态机都能按照相同的日志执行指令,那么它们最终将达到相同的状态。因此,在复制状态机模型下,只要保证了操作日志的一致性,我们就能保证该分布式系统状态的一致性。

图片名称

主从同步

主从同步复制:

  1. Master接受写请求
  2. Master复制日志至Slave
  3. Master等待,直到所有从库返回

问题在于当一个Slave节点失败时,Master节点会阻塞,导致整个集群不可用,虽然保证了一致性,但可用性却大大降低。

多数派

每次写入保证写入大于N/2个节点,每次读保证从大于N/2个节点读。

问题在于在并发环境下,无法保证系统的正确性,执行顺序很重要。

图片名称

强一致性Paxios

Paxos的作者Leslie Lamport早在2001年就写过一篇Paxos Made Simple的论文,来尽可能的简化Paxos的描述。Lamport虚拟了一个叫做Paxos的希腊城邦,这个岛按照议会民主制的政治模式制定法律,但是没有人愿意将自己的全部时间放在这件事上。所以无论是议员,议长或者传递字条的服务员都不能承诺别人需要时一定会出现,也无法承诺批准决议或者传递消息的时间(体现了网络的不可靠性)。

Basic Paxos角色:

  • Client:系统外部角色,请求发起者。类似民众。
  • Proposer:接受Client的请求,向集群提出提议(propose)。并在冲突发生时,起到冲突协调的作用。类似议员,替民众提出议案。
  • Accepetor(Vector):提议投票和接收者,只有在形成法定人数时(Quorum,为多数派),提议才会最终被接受,类似于国会。
  • Learner:提议接受者,backup,备份,对集群的一致性没有影响。类似于记录员。

执行过程:

参考:https://www.jianshu.com/p/ecc6168b33a7

在Basic Paxos协议中,有很多次的执行过程,每次执行过程产生一个单独的执行结果。每次执行过程都有很多轮次,每一轮都有2个阶段。

阶段1

  • 阶段1A:Prepare
    在Prepare阶段,一个Proposer会创建一个Prepare消息,每个Prepare消息都有唯一的提案编号n。n并不是将要提案的内容,而只是一个唯一的编号,用来标志这个Prepare的消息。n必须比该Proposer之前用过的所有编号都大,一般来说我们可以以数字递增的方式来实现这个编号。下来Proposer会把该编号发送给Acceptors,只有大多数Acceptors接收到Proposer发来的消息,该消息才算是发送成功。
  • 阶段1B:Promise
    所有的Acceptors都在等待从Proposers发过来的Prepare消息。当一个Acceptor收到从Proposer发过来的Prepare消息时候,会有两种情况:
    • 该消息中的n是Acceptor所有收到的Prepare消息中最大的一个,那么该Acceptor必须返回一个Promise消息给Proposer,告诉它后面所有小于n的消息我都会忽略掉。如果该Acceptor在过去的某个时间已经确认了某个消息,那么它必须返回那个消息的proposal number m 和 accepted value w (m,w)。如果该Acceptor在过去并没有确认过任何消息,那么会返回NULL。
    • 如果Prepare消息中的n小于该Acceptor之前接收到的消息,那么该消息会被Acceptor忽略(为了优化也可以返回一个拒绝消息给Proposer,告诉它不要再发小于n的消息给我了)。

阶段2

  • 阶段2A:Accept
    如果一个Proposer从Acceptors接收到了足够多的Promises(>n/2),这表示该Proposer可以开始下一个Accept请求的阶段了,在Accept阶段,Proposer需要设置一个值,然后向Acceptors发送Accept请求。在阶段1B我们讲到了,如果Acceptor之前确认过消息,那么会把该消息编号和消息的值(m,w)返回给Proposer, Proposer收到多个Acceptors返回过来的消息之后,会从中选择编号最大的一个消息所对应的值z,并把他作为Accept请求的值(n,z)发给Acceptor。如果所有的Acceptors都没有确认过消息,那么Proposer可以自主选择要确认的值z。
  • 阶段 2b: Accepted
    当Acceptor接收到了Proposer的确认消息请求(n,z),如果该Acceptor在阶段1b的时候没有promise只接收>n的消息,那么该(n,z)消息就必须被Acceptor确认。当(n,z)消息被Acceptor确认时,Acceptor会发送一个Accepted(n,z)消息给Proposer和所有的Learner。当然在大部分情况下Proposer和Learner这两个角色可以合并。
    如果该Acceptor在阶段1b的时候promise只接收>n的消息,那么该确认请求消息会被拒绝或者忽略。

该流程如果失败,将提案编号增加后重试

问题:难以实现,效率低(要经过2轮RPC),可能会出现活锁。

在Basic Paxos协议中,每一次执行过程都需要经历Prepare->Promise->Accept->Accepted 这四个步骤,这样就会导致消息太多,从而影响分布式系统的性能。Multi Paxos提出了Leader的概念

Multi Paxos

如果Leader足够稳定的话,在接下来的执行中,phase 1的请求其实是可以被省略的。这里round number需要+1,表示已经进入下一轮了。

在Basic-Paxos中我们区分了很多角色,有Clients,Proposers, Acceptors and Learners。实际上Proposers, Acceptors and Learners可以合并成一个,我们把它统称为Server。下面是合并之后的序列图:

这种简化更加符合现实中多节点Server的构架。

Raft协议

Paxos 算法的描述偏学术化,缺失了很多细节,无法直接应用于工程领域。实际工程应用中的分布式算法大多是 Paxos 的变种,验证这些算法的正确性也成为了一个难题。Raft协议就是Paxos的简化,类似Multi Paxos。

Raft集群中每个进程只能担任其中一个角色:

  • Leader:发送心跳、管理日志复制与提交
  • Follower:被动响应其他节点发送过来的请求
  • Candidate:主动发起并参与选举

正常运行的情况下,会有一个Leader,其他全为Follower,Follower只会响应Leader和Candidate的请求,而客户端的请求则全部由Leader处理,即使有客户端请求了一个Follower也会将请求重定向到Leader。Candidate代表候选人,出现在选举Leader阶段,选举成功后Candidate将会成为新的Leader。

图片名称

Raft 将一致性问题分解为 3 个独立的子问题:

  • Leader 选举Election:Leader 进程失效后能够自动选举出一个新的 Leader
  • 日志复制Replication:Leader 保证其他节点的日志与其保持一致
  • 状态安全Safety:Leader 保证状态机执行指令的顺序与内容完全一致

p.s. Redis中的sentinel模式使用的就是Raft协议,Raft在Redis内并没有用来实现一些分布式锁以及分布式事务,仅仅是用来做master宕机时的选主,可能后续的版本会逐渐支持。

更多可参考:https://zhuanlan.zhihu.com/p/91288179

ZAB协议

ZAB协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。

ZAB协议定义:ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复和原子广播协议

Zookeeper是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper并没有使用Paxos,而是采用了ZAB协议。

ZAB协议基本与Raft相同,都是Multi Paxos的衍生。ZAB与Raft在一些名词的叫法上有区别:如ZAB将某一个Leader的周期称为epoch,而Raft则称之为term。在实现上也有些许不同:Raft为了保证日志连续性,心跳方向为Leader至Follower,ZAB则相反。

消息广播模式:

ZAB协议的消息广播过程使用的是一个原子广播协议,类似一个2PC二阶段提交过程

(1)Leader将客户端的request转化成一个Proposal(提议);

(2)Leader为每一个Follower准备了一个FIFO队列,并把Proposal发送到队列上;

(3)Leader若收到follower的半数以上ACK反馈;

(4)Leader向所有的follower发送commit。

崩溃恢复:

ZAB 定义了 2 个原则:

  1. ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
  2. ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。

所以,ZAB 设计了下面这样一个选举算法:**能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务。**针对这个要求,如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群中所有机器编号(即 ZXID最大)的事务,那么就能够保证这个新选举出来的 Leader 一定具有所有已经提交的提案。

ZXID:在 ZAB 协议的事务编号 ZXID 设计中,ZXID 是一个 64 位的数字,其中低 32 位可以看作是一个简单的递增的计数器,针对客户端的每一个事务请求,Leader 都会产生一个新的事务 Proposal 并对该计数器进行 + 1 操作。而高 32 位则代表了 Leader 服务器上取出本地日志中最大事务 Proposal 的 ZXID,并从该 ZXID 中解析出对应的 epoch 值,然后再对这个值加一。高 32 位代表了每代 Leader 的唯一性,低 32 代表了每代 Leader 中事务的唯一性。同时,也能让 Follwer 通过高 32 位识别不同的 Leader。简化了数据恢复流程。

基于这样的策略:当 Follower 链接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID 和 Follower 上的 ZXID 进行比对,比对结果要么回滚,要么和 Leader 同步。

其选举流程可参考:https://www.cnblogs.com/veblen/p/10992103.html

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值