【4.分布式存储】-共识算法【精】paxos/mutil-p/fast-p/raft/bully/zab

本文讲分布式的共识概念,算法,应用。涉及到raft,paxos(basic,multi,fast),zk,etcd,chubby。以及思考。关注与如何共识,对于zk,etcd,chubby的接口如何应用于服务发现,分布式锁等略过。html

共识:一个或多个节点提出提案,算法选择一个值。一致赞成,完整(只决定一次),有效(节点投票是有效值,是被提议的值),终止(宕机不回来)。
paxos彻底符合,但raft,zap考虑的是宕机还会回来的状况,用日志保证。能解决上篇(https://segmentfault.com/a/11...)中诸如如下问题:
全序广播至关于重复多伦共识:raft和zap等直接实现全序广播,mutil-paxos也是,都有leader,只决定值的顺序。有固定leader比每一个节点写入,leader维持全序,其余节点同步,冲突少(若没有leader paxos那种要先同步一遍获取最大值,再投票,多了一次pre),画一个有领导和无领导每一个带序号的图能够很好理解带leader会容易些,但单leader有瓶颈。
单领导类的共识:1选出一位领导者,2对领导者的提议进行表决(防止1,一个节点相信本身是领导)投票是同步的,动态成员扩展难,依靠超时检测节点失效,若只有一条特定网络不可靠,会进入领导频繁二人转局面。有领导后也不能领导决定,防止从新恢复等多领导定的状况。
缺点:要多数都赞成,很慢。基本动态扩容很难,手动好比etcdnode

共识算法

raft

  • 共识过程
    数据一致性是经过日志复制的方式,client发给leader(写只发给leader,follower备份恢复用),leader写入日志,同步给follower,当多数follower写入日志并返回给leader时,leader提交数据,返回给客户端确认消息, 发给follower数据已提交,follower提交数据,发回确认给leader。全部的发送都随着跳频发过去。raft中全部server之间的通讯都是RPC调用,而且只有两种类型的RPC调用:第一种是RequestVote,用于选举leader;第二种是AppendEntries。日志和投票结果都须要持续化写在磁盘中,保证宕机后重启任然正常。
    clipboard.png
  • leader选举
    leader(有任期字段term),candidate, follower.每一个节点有在T到2T之间随机选择超时时间。leader和follower经过跳频联系。当一个follower收不到leader的跳频超时时将发起投本身的票。任何一个follower只能投一票,若是发现本身的日志比请求中携带的更新,则拒绝投票。当一轮投票结束有多个候选者时,这几个候选者从新分配随机的超时时间
  • leader同步日志给follower
    在上述数据共识中,当leader确认提交数据后,leader会一直不断地重试提交的rpc给follower、重试,直到请求成功;即便follower宕机了,重启后leader仍会接着发请求,直到请求成功,当leader宕机,如何向follower继续发;1.leader的日志只能增长,=》因此在选择时选term大,log长的 2.leader会把本身的log复制到其余机器,若是新达到多数而且此任期内已有数据过半(挂前的一次数据不会被重复提交)就提交,只提交新任期的,同步follower仍是要同步。
  • log一致性
    每一个日志entry:iterm+index.每次发送AppendEntries时须要带上一次的,follower检查是否同样,同样才接受来保证全部机器log一致
    leader从新选出,为了恢复log一致性,leader为集群中全部follower都保存一个状态变量,即nextIndex:1)nextIndex是leader准备向某个follower发送的下一个log entry的index;2)当leader刚刚即位后,nextIndex的初始值是(1+leader's last index);
    当leader看到请求被拒绝时,其动做很是简单:只需将nextIndex-1,再次尝试。
  • 须要持久化term和投票
    term须要存盘
    任意一个server在一个term内只能投出一票;一旦已经投给了一个candidate,它必须拒绝其余candidate的投票请求;其实server根本不在乎把票投给谁,它只会把票投给最早到请求到它的candidate;为了保证这一点,必须把投票信息持久保存到磁盘上,这样能够保证即便该server投完票后宕机,稍后又当即重启了,也不会在同一个term内给第二个candidate投票了。

paxos

  • basic paxos
    第一阶段收集最新的N和V,第二阶段发起提议:
    clipboard.png
    实际上这里的proposal是leader。共识算法正常是proposor,leader,accepter,leaner(先忽略),用来决议proposer的提议号和是否成功的。每次proposal先到leader(可随机选取,不重要),leader发给accepter若没有冲突返回any不然返回已选的,继续上述过程。
    问题:多个Proposal可能出现死锁一直循环递增N的状况:

    clipboard.png

    上面这个是https://www.microsoft.com/en-...。为了方便理解,去除了实现细节。实时应用中,客户端不会本身处理冲突+1再次投票和发送给其余leaner,这些应该由另外一个角色,在basic中,由一群c协调者,能够和acceptor同样,或者是其中的部分构成,每轮随机一个c做为leader,负责收集本轮结果和通知leaner。proposal->leader(每一个client随机发就能够做为本轮leader)->pre->acceptors返回最大N的值V->带N请求->acceptors->leader->返回给proposal->client失败或者成功或再次投票->投票成功后发给leaner。此过程当中CLIENT2再次发送是另外一个leader。算法

  • fast paxos
    若proposal和acceptor,leader,leaner都是分布式,且要持久化,持久化+发送来回的代价就多了,
    若leader发现没有冲突,再也不参与,proposal直接提交给acceptor(同一轮只投给先到的),直接发送给leaner,能够理解为基于乐观锁的思想,leaner和CLIENT都自行决议,
    若proposal没有决策成功(先到的就是投票,没有半数以上的),1.从新引入leader,异步发送给协调者,协调者选择(由于acceptor只投一次),发给proposal结果(再次引入leader)。2.无leader,在acceptor决议后发送给全部acceptor,其余acceptor收到此消息后对i+1轮的能够比较投票(即便同时刻一个一半也能够再比较投一次,这两种的比较都复杂,要看各个提议的acceptor集合,这部分看论文吧)。
    https://www.microsoft.com/en-...
  • muti-paxos
    当leader稳定,能够省去prepare阶段。简单的说用一个序号来标识是否leader稳定(和raft,zk是同样的,转共识为全序序号的过程),若稳定更新序号直接发送给acceptor,acceptor须要记录序号,若发现有index>当前则返回false从新prepare。由于没有prepare,不知道每次最大的n,不知道leader是否稳定加入了全序。在prepare时并不须要此过程。
    具体作法以下:

    clipboard.png

  chubby就是一个典型的Muti-Paxos算法应用,在Master稳定运行的状况下,只须要使用同一个编号来依次执行每个Instance的Promise->Accept阶段处理。数据库

raft/paxos/zap/mutli-paxos区别

  • raft要有一个leader。在选主时每一个follower只能投一次,不成功随机时间下一次。有主时的共识由主来给日志编号,follower比较就好,follower保证稳定可替换便可。
  • mutli-paxos在去掉pre后和raft类似,都是日志记录,区别是mp容许任何acceptor升级为leader,raft则很严格好比只有最完整日志的才行,mp在preprare后会知道当前最大的index,对于旧的异步补空洞。raft以为补空洞过程太繁琐,增长了选主的复杂度。
  • paxos leader不能那么重要(fast paxos在无冲突时甚至无leader参与),每次能够随机选,只是汇总投票。paxos在fast模式下,冲突处理时,每一个acceptor能够更新选票从新投(实际上是冲突的解决,也能够不算投票,根据集合等复杂的逻辑,在zk中就按照现有集合票数)。
  • zap仍是有leader的。zap在无主的时候选举算法和fast paxos很像,有最大xid(相似pre阶段,只不过是上次存好的),先选主,每次选主的提案直接给acceptor而且采用无协调者的冲突处理。在有主时,用paxos的思想先pre收集并同步信息保证一致,主处理写,多数处理成功后回复。
  • paxos优点就是单主能不能抗住了,单主投票只能一次一个。

zookeeper

zk定位于分布式协调服务
官方:https://zookeeper.apache.org/...
下面介绍zk的经常使用功能,架构,共识过程。apache

架构

自己的数据组织以文件形式,每一个叶节点znodes,非页节点只是命名空间的路径标识;可是存储于内存,记录磁盘日志,副本包含完整内存数据和日志,znodes维护节点的版本,zxid等全部信息。
Zookeeper对于每一个节点QuorumPeer的设计至关的灵活,QuorumPeer主要包括四个组件:客户端请求接收器(ServerCnxnFactory)、数据引擎(ZKDatabase)、选举器(Election)、核心功能组件(Leader/Follower/Observer不一样)

做用

1.单独zk集群元数据的可靠性和一致性保证,元数据保存在zk全部副本中(少许彻底能够放在内存中数据)
路由,选择数据库,调度程序
2.单独zk集群,锁,防御令牌,获取锁或者zxid
3.变动通知,每一个变动都会发送到全部节点
watch机制
4.用于检测,服务发现
session:
每一个ZooKeeper客户端的配置中都包括集合体中服务器的列表。在启动时,客户端会尝试链接到列表中的一台服务器。若是链接失败,它会尝试链接另外一台服务器,以此类推,直到成功与一台服务器创建链接或由于全部ZooKeeper服务器都不可用而失败。
只要一个会话空闲超过必定时间,均可以经过客户端发送ping请求(也称为心跳)保持会话不过时。ping请求由ZooKeeper的客户端库自动发送,所以在咱们的代码中不须要考虑如何维护会话。这个时间长度的设置应当足够低,以便能档检测出服务器故障(由读超时体现),而且可以在会话超时的时间段内从新莲接到另一台服务器。segmentfault

zookeeper数据同步过程

采用了递增的事务id号(zxid)来标识事务。全部的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。服务器

  • zab protocol网络

    Leader election
        leader选举过程,electionEpoch自增,在选举的时候lastProcessedZxid越大,越有可能成为leader
    Discovery:
        第一:leader收集follower的lastProcessedZxid,这个主要用来经过和leader的lastProcessedZxid对比来确认follower须要同步的数据范围
        第二:选举出一个新的peerEpoch,主要用于防止旧的leader来进行提交操做(旧leader向follower发送命令的时候,follower发现zxid所在的peerEpoch比如今的小,则直接拒绝,防止出现不一致性)
    Synchronization:
        follower中的事务日志和leader保持一致的过程,就是依据follower和leader之间的lastProcessedZxid进行,follower多的话则删除掉多余部分,follower少的话则补充,一旦对应不上则follower删除掉对不上的zxid及其以后的部分而后再从leader同步该部分以后的数据
    Broadcast
        正常处理客户端请求的过程。leader针对客户端的事务请求,而后提出一个议案,发给全部的follower,一旦过半的follower回复OK的话,leader就能够将该议案进行提交了,向全部follower发送提交该议案的请求,leader同时返回OK响应给客户端

实际上zookeeper中算法三阶段:FSE=>Recovery=>Broadcast(广播和上面的一致)session

  • fast leader election
    基于fast paxos。发送给全部的节点。没有随机leader参与收集。
    clipboard.png架构

    LOOKING:进入leader选举状态
    FOLLOWING:leader选举结束,进入follower状态
    LEADING:leader选举结束,进入leader状态
    OBSERVING:处于观察者状态
    1.serverA首先将electionEpoch自增,而后为本身投票
    2 serverB接收到上述通知,而后进行投票PK
    若是serverB收到的通知中的electionEpoch比本身的大,则serverB更新本身的electionEpoch为serverA的electionEpoch
    若是该serverB收到的通知中的electionEpoch比本身的小,则serverB向serverA发送一个通知,将serverB本身的投票以及electionEpoch发送给serverA,serverA收到后就会更新本身的electionEpoch
    在electionEpoch达成一致后,就开始进行投票之间的pk,优先比较proposedEpoch,而后优先比较proposedZxid,最后优先比较proposedLeader
    pk完毕后,若是本机器投票被pk掉,则更新投票信息为对方投票信息,同时从新发送该投票信息给全部的server。若是本机器投票没有被pk掉,若是是looking,过半更改状态,若是FOLLOWING/LEADING说明落后,加速收敛
  • Recovery
    略:https://my.oschina.net/pingpa...
  • follower读写过程图:

clipboard.png

ectd

常常应用于配置共享和服务发现,相比于zk,简单。使用 Go 语言编写部署简单;使用 HTTP 做为接口使用简单;使用 `Raft 算法`保证强一致性让用户易于理解。无需安装客户端。提供接口K-V存储(storing up to a few GB of data with consistent ordering,提供线性读),watch,lease,lock,election。由于共识彻底实现的raft因此只简单说下部署模式,节点组成,数据持久化等。
官方:https://coreos.com/etcd/docs/latest/

架构图:

单节点以下
clipboard.png
store:为用户提供API
集群会区分proxy,leader,follower

  • 启动
    etcd 有三种集群化启动的配置方案,分别为静态配置启动、etcd 自身服务发现、经过 DNS 进行服务发现
    自身服务发现:
    首先就用自身单个的 url 构成一个集群,而后在启动的过程当中根据参数进入discovery/discovery.go源码的JoinCluster函数。由于咱们事先是知道启动时使用的 etcd 的 token 地址的,里面包含了集群大小 (size) 信息。在这个过程实际上是个不断监测与等待的过程。启动的第一步就是在这个 etcd 的 token 目录下注册自身的信息,而后再监测 token 目录下全部节点的数量,若是数量没有达标,则循环等待。当数量达到要求时,才结束,进入正常的启动过程。
  • 运行
    proxy、leader\follower。proxy只负责转发,etcd proxy支持2种运行模式:readwrite和readonly,缺省的是readwrite,即proxy会将全部的读写请求都转发给etcd集群;readonly模式下,只转发读请求,写请求将会返回http 501错误。proxy保证参与投票数量有限的性能,全部follower都同步完数据才返回成功(由于异常不自动回来,能够所有,已经被管理员补齐了,不然只能读主,所以节点不能不少),在正常节点故障后,能够由管理员手动处理,一个备份的功能。
    etcd 能够代理访问 leader 节点的请求,因此若是你能够访问任何一个 etcd 节点,那么你就能够无视网络的拓扑结构对整个集群进行读写操做,不然只能链接leader.
    无端障自恢复(容易出错,考虑到已经高可用,管理员有时间自行恢复)
  • 数据持久:WAL+snapshot(删除WAL)
    从 snapshot 中得到集群的配置信息,包括 token、其余节点的信息等等,而后载入 WAL 目录的内容,从小到大进行排序。根据 snapshot 中获得的 term 和 index,找到 WAL 紧接着 snapshot 下一条的记录,而后向后更新,直到全部 WAL 包的 entry 都已经遍历完毕,Entry 记录到 ents 变量中存储在内存里。此时 WAL 就进入 append(read和append互斥) 模式,为数据项添加进行准备。
    当 WAL 文件中数据项内容过大达到设定值(默认为 10000)时,会进行 WAL 的切分,同时进行 snapshot 操做。这个过程能够在etcdserver/server.go的snapshot函数中看到。因此,实际上数据目录中有用的 snapshot 和 WAL 文件各只有一个,默认状况下 etcd 会各保留 5 个历史文件
  • 数据KV
    内存BTREE索引,物理B+树,存历史版本,没有引用后压缩删除历史版本。
  • 应用:https://www.infoq.cn/article/...

chubby

GFS和Big Table等大型系统都用他来解决分布式协做、元数据存储和Master选择等一系列与分布式锁服务相关的问题
客户端发送到其余机器都会将master反馈,从新转到master,持续直到换。
数据组织方式和zk同样。
只有主节点提供读写(数据和日志有空洞),高可靠和可用,吞吐量不如zk。在换主阶段会阻塞。

etcd,chubby,zk的对比

由于zk,etcd都会补齐follower。所以主从均可以读。etcd的主是固定的,除非故障=》raft的换主。chubby(06年)用过的mutil-poxas主通常不变,不保证每轮主从数据一致,只有主有读写能力,吞吐量会差一些,一万台机器分布式锁仍是能够的。etcd(14年)是后来的,确定更好啊,有http接口,一切都更轻量简单,缺点只是无端障自恢复吧,zk每次都会选主(但基于一个xid,基本也相似mutil-poxas会稳定),可自动恢复。

相关文章
相关标签/搜索