分布式一致性算法——学习笔记

分布式一致性问题的引入

以Raft算法为例,讲解分布式一致性问题的引入:

确定主机

  • 当分布式系统中的一天计算机数据发生了改变时,其他计算机上的数据出现不一致
  • 选取一个"老大"进行一致性管理,具体如何管理,如果这个"老大"自己先挂了,分布式系统的一致性问题应当如何处理?
  • 替补机制:让谁来替补? 引入一个竞争评分机制,自动完成。
    • 初始情况下,每个节点都是候选人,都可以向其他节点发出邀请,让大家投自己,票数过半就是老大
    • 为了防止大家同时发起投票邀请,设立了“选举超时时间”,就是一个等待时间
    • 每个节点都有计数器,从0开始计时,谁的等待时间到了,就率先发起竞选,通知其他节点选自己
    • 率先发起竞选的节点会增加自己的任期Term (int) ,初始值为0
    • 其他节点接收到投票邀请,如果还没到达自身的等待时间,便只好同意投票请求,同时重置自身的计时器,等待下一轮投票
    • 一旦的票数超过一半,发起竞选的节点便知道自己成为了"老大"
      开始向分布式系统中的其他节点发送信息,其他节点返回响应信息,双向维持心跳
    • 其他节点每次收到心跳信息都会重置自己的计时器
  • 此时形成了老大和小弟,如果老大不慎挂了,那么其他节点会根据上述机制立刻展开竞争
  • 如果某几个节点得到了相同的票数,就重新发起竞争,重新分配

数据的复制

  • 由于是分布式系统,只有大多数节点都成功保存了数据,才算保存成功
  • 所以直接点必须承担起协调的职责
  • 复制日志的方法,每个节点都有一个日志的队列,在真正把数据提交之前,先把数据追加到日志队列中,然后向各个子节点复制
    1. 客户端发送数据给主节点A
      节点A先把数据记录到日志中,即此时处于“未提交状态”
    2. 在下一次的心跳消息中,数据被发送至各个“小弟”
    3. 各个子节点也把数据记录到日志中(也处于未提交状态),然后向主节点报告自己已经记录了日志
    4. 如果主节点A收到响应超过了半数,节点A就提交数据,通知客户端保存数据成功
    5. 主节点A在下一次心跳信息中,通知各个子节点该数据已提交。各个子节点也提交自己的数据
      如果某个子节点挂掉了,那么主节点会尝试重新链接它,一旦它重新开始工作,就需要从主节点那里去复制数据,和主节点保持一致。

1. 什么是一致性(基础)

CAP Theorem ,内容是指:
对于一个分布式系统,不可能同时满足以下三点:

  • 一致性(Consistency) [保证]
  • 可用性(Availability) [尽力保证]
  • 分区容错性(Partition Tolerance) [保证]

1.1 一致性模型

  • 若一致性
    • 最终一致性 (当向分布式DB中写入一条数据时,立刻访问这条数据,系统不能保证用户可以立刻访问到这条数据,但系统承诺,在将来的某一时刻用户最终可以访问到这条数据)
      • DNS (Domain Name System)
      • Gossip (Cassandra的通信协议)
  • 强一致性
    • 同步
    • Paxos
    • Raft (multi-paxos)
    • ZAB (multi-paxos)

1.2 一致性模型的核心问题:

数据不能存储在单个节点上,分布式系统对fault tolerance的一般解决方案是state machine replication

强一致性算法准确的讲,应当是 state machine replication的共识(consensus)算法

Paxos其实是一个共识算法。系统的最终一致性,不仅需要达成共识,还会取决于client客户端的行为

1.3 强一致性算法——主从同步

主从同步复制:

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

问题:
一个节点失败,Master阻塞,导致整个集群不可用,保证了一致性,可用性却大大降低

1.4 强一致性算法——多数派

基本思想: 多数派(一半),每次写都保证写入大于N/2个节点,每次读保证从大于N/2个节点中读

问题:
在并发环境下,无法保证系统正确性,顺序非常重要
某分布式系统,同时接收到了 归零加5 指令,由于系统庞大,
有的节点先接收了 归零 后接收了 加5 指令, 结果为5
有的节点先接收了 加5 后接收了 归零 指令, 结果为0
造成系统不一致

2. 强一致性算法——Paxos

Lesile Lamport虚拟了一个叫做Paxos的希腊城邦:
此城邦按照议会民主制的政治模式制定法律,但是没有人愿意将自己的全部时间和精力放在这种事上。所以无论是议员,议长还是传递纸条的服务员都不能保证别人需要时一定出现,也无法承诺批准决议或者传递消息的时间

Paxos

  • Basic Paxos
  • Multi Paxos
  • Fast Paxos

2.1 Basic Paxos

  1. 角色介绍:
  • Client : 系统外部角色,请求发起者 (民众)

  • Proposer: 接受Client的请求,向集群提出提议(Propose)。并在冲突发生时,起到冲突调节的作用。 (议员,替民众提出议案)

  • Acceptor(Voter):投票者和接收者,只有在形成法定人数(Quorum,一般为多数派,即超过一半)时,提议才会被接受。 (像议会)

  • Learner: 提议接受者, 负责备份,对集群一致性没什么影响。 (记录员)

  1. 步骤与阶段(phases):

    第一阶段:

    • Phase 1-1: Prepare
      proposer提出一个提案,编号为N,此N大于这个proposer之前提出提案编号。请求acceptors的quorum(法定人数)接受。

    • Phase 1-2: Promise
      如果N大于此Acceptor之前接受的任何提案编号则接受,否则拒绝

    第二阶段:

    • Phase 2-1: Accept
      如果达到了多数派,proposer会发出accept请求,此请求包含提案号N,以及提案内容

    • Phase 2-2: Accepted
      如果acceptor在此期间没有收到任何编号大于N的提案,则接受此提案内容,否则忽略

      1572237220806.png

2.1.1 节点失效的情况

  • 若部分节点失败,但总体达到了法定人数Quoroms
    没啥区别

  • Proposer失败

    1572237423137.png

​ 在Accept阶段Proposer挂了,提案内容不会被写入,另一个Proposer会接替,重新发起提案请求prepare2
之后正常进行

2.1.2 问题

  • 活锁Liveness 或 竞争dueling

    活锁:

    讨论提案1, acceptor同意了,但提案1的内容没有提交,此时出现议员要求讨论提案2,acceptor同意,把1放弃了,proposer将提案1更改为提案3再次提交,循环,什么操作都没能最终提交

    用random timeout 解决,被打断的提议随机等待一段时间后再重新提交

  • 难实现,效率低,使用两次RPC(远程功能调用)

2.2 Multi Paxos

  • (New) Leader: 唯一的proposer,所有请求都需经过此Leader

    先进行一轮leader竞选,使用两轮RPC,之后确定Leader,只调用一轮RPC,进行提案内容提交

  • Proposer和Acceptor合并为 Servers , Servers之间进行竞选


    1572239638175.png

3. 强一致性算法——RAFT

一致性算法:允许一组计算机像一个整体一样工作,即是其中一些机器出现故障也能够继续工作下去
在上述数据复制的5步中,如果主节点挂了,数据就不一致了怎么办

  • RAFT
    主节点:Leader
    子节点:Follower
  • 旨在取代目前广为使用的Paxos算法,在各种主流语言中都有开源实现JGroup的Raft协议实现

3.1 Raft原理

在raft中每个节点都处于一下三种状态之一:

  • follower:所有节点都从follower状态开始,如果没有收到来自leader的消息,就会变成candidate状态
  • candidate:会向其他节点“拉选票”,如果得到大部分的票则成为Leader,这个过程就叫做Leader选举(Leader Election)
  • Leader:所有对系统的修改都会先经过leader,每个修改都会写一条日志(Log entry)。Leader收到修改请求以后的过程如下,这个过程叫做日志复制(Log Replication)
    1. 复制日志到所有follower节点 (replication entry)
    2. 大部分节点响应时才提交日志
    3. 通知所有follower节点日志已提交
    4. 所有follower也提交日志
    5. 现在整个系统处于一致状态

3.2 Leader Election

成为candidate的结点发起新的选举期(election term)去“拉选票”:

  1. 重置自己的计时器
  2. 投自己一票
  3. 发送 Request Vote消息

如果接收结点在新term内没有投过票那它就会投给此candidate,并重置它自己的选举超时时间。candidate拉到大部分选票就会成为leader,并定时发送心跳——Append Entries消息,去重置各个follower的计时器。当前Term会继续直到某个follower接收不到心跳并成为candidate。

如果不巧两个结点同时成为candidate都去“拉票”怎么办?这时会发生Splite Vote情况。两个结点可能都拉到了同样多的选票,难分胜负,选举失败,本term没有leader。之后又有计时器超时的follower会变成candidate,将term加一并开始新一轮的投票。

3.3 Log Replication

当发生改变时,leader会复制日志给follower结点,这也是通过Append Entries心跳消息完成的。前面已经列举了Log Replication的过程,这里就不重复了。

Raft能够正确地处理网络分区(“脑裂”)问题。假设A~E五个结点,B是leader。如果发生“脑裂”,A、B成为一个子分区,C、D、E成为一个子分区。此时C、D、E会发生选举,选出C作为新term的leader。这样我们在两个子分区内就有了不同term的两个leader。这时如果有客户端写A时,因为B无法复制日志到大部分follower所以日志处于uncommitted未提交状态。而同时另一个客户端对C的写操作却能够正确完成,因为C是新的leader,它只知道D和E。

当网络通信恢复,B能够发送心跳给C、D、E了,却发现“改朝换代”了,因为C的term值更大,所以B自动降格为follower。然后A和B都回滚未提交的日志,并从新leader那里复制最新的日志。但这样是不是就会丢失更新?

3.4 Safety

https://raft.github.io

涉及到1.2中提到的,一致性最终可能牵扯到具体的Client行为

一致性并不一定表示完全正确性

三个可能的结果:成功,失败,unknown

5节点Raft实例: unknown情况

--Client写请求,leader向followers同步日志,此时集群中有3个节点失败,2个节点存活,结果是?


Log被成功复制,但无法提交,因为达不到一半

--恢复失败的节点。之前的Log被提交,写入成功  (在中间阶段就是unknown)



-- 同时进入两个client请求, 期间有节点失效,造成整个系统日志不一致,但在节点恢复时,少数节点的日志被放弃,重新向多数节点的日志看齐

4. 强一致性算法——ZAB

(zookeeper)

基本与Raft相同

  • 命名区别:
    leader周期:

    • ZAB: epoch
    • raft: term
  • 实现区别:

    • raft: 保证日志连续性,心跳方向为leader向follower

    • ZAB: 相反

5. 项目实践

  • Zookeeper集群搭建和命令行操作 —— ZAB
  • Etcd集群搭建和restful api ——Raft

(上课去了,还没写完)

你可能感兴趣的:(分布式一致性算法——学习笔记)