赞
踩
分布式系统通常由异步网络连接的多个节点构成,每个节点有独立的计算和存储,节点之间通过网络通信进行协作。分布式一致性指多个节点对某一变量的取值达成一致,一旦达成一致,则变量的本次取值即被确定。
在分布式存储系统中,通常以多副本冗余的方式实现数据的可靠存储。同一份数据的多个副本必须保证一致,而数据的多个副本又存储在不同的节点中,这里的分布式一致性问题就是存储在不同节点中的数据副本(或称为变量)的取值必须一致。不仅如此,因为变量是可变的,变量会有多次取值,变量的多次取值构成一个序列,分布式一致性还要求多个节点对该变量的取值序列必须一致。
在大量客户端并发请求读/写的情况下,维护数据多副本的一致性无疑非常重要,且富有挑战。
一致性问题隐含了一个非常重要的要素:时间。比如多副本一致性在考虑时间要素后可描述为:在某一个时间点,多个节点上看到的数据副本其取值必须达成一致。数据取值(或称为变量取值)通常由某个事件(如客户端的写请求)先行触发,之后因节点之间消息延时之长短差异,先后将数据取值依次更新到分布式系统中的多个节点。
分布式系统中时间、事件、顺序等概念在Leslie Lamport于1978年发表的论文“Time Clocks and the Ordering of Events in a Distributed System”中有系统性论述。Lamport自述其灵感源于以狭义相对论的角度从事物本质上去理解分布式系统中使用消息时间戳来表述事件的全序关系。狭义相对论告诉我们时空中的事件不存在一个始终如一的全序关系,事件之间的先后关系完全取决于事件间的因果关系(即事件2是由事件1引起的),希望通过时间戳来描述事件的全序关系本质上与事件间因果关系是一致的。文中,Lamport提出了逻辑时钟(logical clock)的概念,并给出定义事件全序关系的算法,Lamport声称基于这样的算法可以实现任意的分布式系统。
分布式系统中,事件之间的顺序至关重要。举例说,假设分布式存储系统由三个节点s1、s2和s3构成,现有两个客户端c1和c2,客户端c1首先发起写请求x=v1希望将变量x的值从v0更新为v1,紧接着c2发起请求读取变量x的值。变量x在节点s1、s2、s3上都有副本,c2可能从任意节点读取x的值。考虑到网络延时的影响,c2的读请求消息可能先于c1的写请求到达节点s3,如果没有全局的顺序控制,c2就有可能读到变量x的旧值,这显然是不希望看到的。
ACID是数据库(DBMS)事务正确执行所必须满足的四个特性的首字母缩写。
Atomicity(原子性):一个事务的所有操作,要么全部完成,要么全部不完成。所谓事务,是指由一系列数据操作所组成的完整逻辑过程。比如银行转账事务由两个操作组成:从源账户扣除金额,以及向目标账户增加金额。
Consistency(一致性):指事务开始之前和事务结束之后,数据的完整性约束没有被破坏。包含两层含义:a)数据库机制层面,事务执行前后,数据能符合设置的约束,如唯一约束、外键约束;b)业务层面,由应用开发人员保证业务一致性。还是以银行转账为例,A、B两个账号,转账之前和之后,A、B两个账号余额总额必须一致。
Isolation(隔离性):数据库能够防止由于多个并发事务交叉执行而导致数据的不一致。
Durability(持久性):指事务结束后,对数据的修改是永久的,不会回滚到之前的状态。
CAP理论主张基于网络的数据共享系统,都最多只能拥有以下三条中的两条:
Consistency:指数据一致性,也就是开篇第1节所讨论的分布式一致性概念。Brewer对Consistency的描述为“等同于所有节点访问同一份最新的数据副本”
Available:对数据更新具备高可用性
Partitions tolerance:指容忍网络分区
CAP“三选二”模式指AP、CP和AC。假设在分区模式下,有两个节点分别位于分区两侧,如果为了可用性就必须允许其中一个节点更新数据,然而因为网络分区两个节点无法通信,所以会导致两个节点数据不一致,即无法做到C(Consistency);而为了保证数据一致性,只能将分区一侧的节点设置为不可用,则又无法做到A(Available)。只有两个节点能够通信,才能同时保证Available和Consistency,这又违背了分区容忍P(Partitions tolerance)的特性。
BASE:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)三个短语的缩写,来自 ebay 的架构师提出。
Base 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:
既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
什么是基本可用呢?假设系统,出现了不可预知的故障,但还是能用,相比较正常的系统而言:
响应时间上的损失:正常情况下的搜索引擎 0.5 秒即返回给用户结果,而基本可用的搜索引擎可以在 1 秒作用返回结果。
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单,但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
什么是软状态呢?相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种 “硬状态”。
软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
这个比较好理解了哈。
上面说软状态,然后不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。
稍微官方一点的说法就是:
系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。
DNS(Domain Name System)
Gossip(Cassandra的通信协议)
比如DNS在某一个路由节点中增加了一条记录,其他路由节点需要经历多次路由表同步后,才能获取到这一条新增的记录
1. 因果一致性(Causal consistency)
指的是:如果节点 A 在更新完某个数据后通知了节点 B,那么节点 B 之后对该数据的访问和修改都是基于 A 更新后的值。于此同时,和节点 A 无因果关系的节点 C 的数据访问则没有这样的限制。
2. 读己之所写(Read your writes)
这种就很简单了,节点 A 更新一个数据后,它自身总是能访问到自身更新过的最新值,而不会看到旧值。其实也算一种因果一致性。
3. 会话一致性(Session consistency)
会话一致性将对系统数据的访问过程框定在了一个会话当中:系统能保证在同一个有效的会话中实现 “读己之所写” 的一致性,也就是说,执行更新操作之后,客户端能够在同一个会话中始终读取到该数据项的最新值。
4. 单调读一致性(Monotonic read consistency)
单调读一致性是指如果一个节点从系统中读取出一个数据项的某个值后,那么系统对于该节点后续的任何数据访问都不应该返回更旧的值。
5. 单调写一致性(Monotonic write consistency)
指一个系统要能够保证来自同一个节点的写操作被顺序的执行。
然而,在实际的实践中,这 5 种系统往往会结合使用,以构建一个具有最终一致性的分布式系统。实际上,不只是分布式系统使用最终一致性,关系型数据库在某个功能上,也是使用最终一致性的,比如备份,数据库的复制过程是需要时间的,这个复制过程中,业务读取到的值就是旧的。当然,最终还是达成了数据一致性。这也算是一个最终一致性的经典案例。
同步
Paxos
Raft(multi-paxos)
ZAB(multi-paxos)
主从同步复制
1.Master接受写请求
2.Master复制日志到Slave
3.Master等待,直到所有从库返回
问题:一个节点失败,Master阻塞,导致整个集群不可用,保证了一致性,可用性大大降低。
每次写入保证写入大于N/2个节点 每次读保证从大于N/2个节点读
问题:
在并发环境下,无法保证系统的正确性,顺序很重要。
Paxos算法解决的问题正是分布式一致性问题,即一个分布式系统中的各个进程如何就某个值(决议)达成一致。
Paxos算法运行在允许宕机故障的异步系统中,不要求可靠的消息传递,可容忍消息丢失、延迟、乱序以及重复。它利用大多数 (Majority) 机制保证了2F+1的容错能力,即2F+1个节点的系统最多允许F个节点同时出现故障。
一个或多个提议进程 (Proposer) 可以发起提案 (Proposal),Paxos算法使所有提案中的某一个提案,在所有进程中达成一致。系统中的多数派同时认可该提案,即达成了一致。最多只针对一个确定的提案达成一致。
Paxos将系统中的角色分为提议者 (Proposer),决策者 (Acceptor),和最终决策学习者 (Learner):
Proposer: 提出提案 (Proposal)。Proposal信息包括提案编号 (Proposal ID) 和提议的值 (Value)。
Acceptor:参与决策,回应Proposers的提案。收到Proposal后可以接受提案,若Proposal获得多数Acceptors的接受,则称该Proposal被批准。
Learner:不参与决策,从Proposers/Acceptors学习最新达成一致的提案(Value)。
Paxos算法通过一个决议分为两个阶段(Learn阶段之前决议已经形成):
第一阶段:Prepare阶段。Proposer向Acceptors发出Prepare请求,Acceptors针对收到的Prepare请求进行Promise承诺。
第二阶段:Accept阶段。Proposer收到多数Acceptors承诺的Promise后,向Acceptors发出Propose请求,Acceptors针对收到的Propose请求进行Accept处理。
第三阶段:Learn阶段。Proposer在收到多数Acceptors的Accept之后,标志着本次Accept成功,决议形成,将形成的决议发送给所有Learners。
Paxos算法流程中的每条消息描述如下:
Proposer生成全局唯一且递增的Proposal ID (可使用时间戳加Server ID),向所有Acceptors发送Prepare请求,这里无需携带提案内容,只携带Proposal ID即可。
Acceptors收到Prepare请求后,做出“两个承诺,一个应答”。
两个承诺:
不再接受Proposal ID小于等于(注意:这里是<= )当前请求的Prepare请求。
不再接受Proposal ID小于(注意:这里是< )当前请求的Propose请求。
一个应答:
不违背以前作出的承诺下,回复已经Accept过的提案中Proposal ID最大的那个提案的Value和Proposal ID,没有则返回空值。
Proposer 收到多数Acceptors的Promise应答后,从应答中选择Proposal ID最大的提案的Value,作为本次要发起的提案。如果所有应答的提案Value均为空值,则可以自己随意决定提案Value。然后携带当前Proposal ID,向所有Acceptors发送Propose请求。
Acceptor收到Propose请求后,在不违背自己之前作出的承诺下,接受并持久化当前Proposal ID和提案Value。
Proposer收到多数Acceptors的Accept后,决议形成,将形成的决议发送给所有Learners。
- 获取一个Proposal ID n,为了保证Proposal ID唯一,可采用时间戳+Server ID生成;
- Proposer向所有Acceptors广播Prepare(n)请求;
- Acceptor比较n和minProposal,如果n>minProposal,minProposal=n,并且将 acceptedProposal 和 acceptedValue 返回;
- Proposer接收到过半数回复后,如果发现有acceptedValue返回,将所有回复中acceptedProposal最大的acceptedValue作为本次提案的value,否则可以任意决定本次提案的value;
- 到这里可以进入第二阶段,广播Accept (n,value) 到所有节点;
- Acceptor比较n和minProposal,如果n>=minProposal,则acceptedProposal=minProposal=n,acceptedValue=value,本地持久化后,返回;否则,返回minProposal。
- 提议者接收到过半数请求后,如果发现有返回值result >n,表示有更新的提议,跳转到1;否则value达成一致。
图中P代表Prepare阶段,A代表Accept阶段。3.1代表Proposal ID为3.1,其中3为时间戳,1为Server ID。X和Y代表提议Value。
实例1中P 3.1达成多数派,其Value(X)被Accept,然后P 4.5学习到Value(X),并Accept。
实例2中P 3.1没有被多数派Accept(只有S3 Accept),但是被P 4.5学习到,P 4.5将自己的Value由Y替换为X,Accept(X)。
实例3中P 3.1没有被多数派Accept(只有S1 Accept),同时也没有被P 4.5学习到。由于P 4.5 Propose的所有应答,均未返回Value,则P 4.5可以Accept自己的Value (Y)。后续P 3.1的Accept (X) 会失败,已经Accept的S1,会被覆盖。
回顾两个承诺之一,Acceptor不再应答Proposal ID小于等于当前请求的Prepare请求。意味着需要应答Proposal ID大于当前请求的Prepare请求。
两个Proposers交替Prepare成功,而Accept失败,形成活锁(Livelock)。
原始的Paxos算法(Basic Paxos)只能对一个值形成决议,决议的形成至少需要两次网络来回,在高并发情况下可能需要更多的网络来回,极端情况下甚至可能形成活锁。如果想连续确定多个值,Basic Paxos搞不定了。因此Basic Paxos几乎只是用来做理论研究,并不直接应用在实际工程中。
实际应用中几乎都需要连续确定多个值,而且希望能有更高的效率。Multi-Paxos正是为解决此问题而提出。Multi-Paxos基于Basic Paxos做了两点改进:
1.针对每一个要确定的值,运行一次Paxos算法实例(Instance),形成决议。每一个Paxos实例使用唯一的Instance ID标识。
2.在所有Proposers中选举一个Leader,由Leader唯一地提交Proposal给Acceptors进行表决。这样没有Proposer竞争,解决了活锁问题。在系统中仅有一个Leader进行Value提交的情况下,Prepare阶段就可以跳过,从而将两阶段变为一阶段,提高效率。
Multi-Paxos首先需要选举Leader,Leader的确定也是一次决议的形成,所以可执行一次Basic Paxos实例来选举出一个Leader。选出Leader之后只能由Leader提交Proposal,在Leader宕机之后服务临时不可用,需要重新选举Leader继续服务。在系统中仅有一个Leader进行Proposal提交的情况下,Prepare阶段可以跳过。
Multi-Paxos通过改变Prepare阶段的作用范围至后面Leader提交的所有实例,从而使得Leader的连续提交只需要执行一次Prepare阶段,后续只需要执行Accept阶段,将两阶段变为一阶段,提高了效率。为了区分连续提交的多个实例,每个实例使用一个Instance ID标识,Instance ID由Leader本地递增生成即可。
Multi-Paxos允许有多个自认为是Leader的节点并发提交Proposal而不影响其安全性,这样的场景即退化为Basic Paxos。
Chubby和Boxwood均使用Multi-Paxos。ZooKeeper使用的Zab也是Multi-Paxos的变形。
Raft是一种管理复制日志的一致性算法。
它的首要设计目的就是易于理解,所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。
Raft将一致性拆分为几个关键元素:
Leader选举
日志复制
安全性
Raft通过选举Leader并由Leader节点负责管理日志复制来实现多副本的一致性。
在Raft中,节点有三种角色:
Leader:负责接收客户端的请求,将日志复制到其他节点并告知其他节点何时应用这些日志是安全的
Follower:负责响应来自Leader或者Candidate的请求
Candidate:用于选举Leader的一种角色
过程展示:http://thesecretlivesofdata.com/raft/
场景模拟:https://raft.github.io/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。