引言
计算机的系统架构从集中式发展到了分布式
集中式特点:整个系统的业务单元都集中部署在一个中心节点上,数据也集中存储在这个中心节点上。
集中式的缺点:一旦一台大型主机出现了故障,那么整个系统将出于不可用状态;随着计算机系统的规模扩大,主机的扩容也比较困难。
分布式的特点:
分布性:一个分布式系统中的计算机在空间部署上是可以随意分布的
对等性:分布式系统的计算机没有主从之分,所有的计算机节点都是对等的,副本是分布式系统对数据和服务提供的一种冗余方式,为了提供高可用服务,通常需要副本。
并发性:一个分布式系统的多个节点,可能会并发地操作一些共享的资源,如数据库和分布式缓存
缺乏中心时钟:在分布式系统中,很难定义两个事件究竟谁先谁后,因为缺乏一个全局的时钟序列控制
故障总是会发生:组成分布式系统的所有计算机,都有可能发生任何形式的故障
分布式环境的各种问题
1、通信异常:分布式系统需要各个节点之间进行网络通信,因此每次网络通信都伴随着网络不可用的风险
2、网络分区:当网络发生异常,节点之间的网络延迟不断增大,导致只有部分节点之间能够进行正常通信,另些节点则不能,这种现象叫做网路分区。
当网络分区出现时,分布式系统内会出现局部小集群,每个小集群都有主节点,这种现象称为脑裂。
3、三态:分布式系统的每一次请求与响应,会存在成功、失败和超时三种形态
4、节点故障:组成分布式系统的节点出现的宕机和僵死现象
分布式事务
事务的ACID特性和四个隔离级别
在分布式数据库中,数据分散在不同的机器上,为了保证分布式系统的可靠性,传统的单机事务模型已经无法胜任,就需要处理分布式事务。
分布式事务:事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上,通常一个分布式事务会涉及多个数据源或者多个业务系统的操作。
CAP和BASE理论:
对于如何构建一个兼顾可用性和一致性的系统成了一个难题,因此出现了CAP和BASE分布式系统理论。
CAP理论:
CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency),可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能满足其中的两项。
一致性:在分布式环境中,一致性是指数据在多个副本之间能够保持一致。
可用性:系统提供的服务必须一直出于可用的状态,对于用户的每一次请求总是能够在指定的响应时间内返回一个正常的响应结果
分区容错性:分布式系统在遇到任何网络分区故障的时候,仍然能够正常对外提供服务,除非整个网络环境都发生了故障。
当出现网络分区时,对系统的请求要么抛弃满足一致性,要么只更新部分节点满足可用性,因此只能在一致性和可用性中二选一。
对分布式系统而言,满足分区容错性是一个最基本的要求。因为在分布式系统中的各个组件必然需要被部署到不同的节点上,各个节点之间必然要进行网络通信,而网络异常又是一个必然会出现的异常情况,因此分区容错性是一个分布式系统必然要面对和解决的问题。因此系统架构师只能在一致性C和可用性A之间寻求平衡。
BASE理论:
BASE是Basically Available(基本可用)、Soft sate(软状态)、Eventually consistent(最终一致性)的简写。
BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结。基于CAP演化而来,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身业务的特点,采用适当的方式来使系统达到最终一致性。
BA:基本可用
指分布式系统在出现不可预知故障的时候,允许损失部分可用性。如:
1、响应时间上的损失:正常情况下,一个搜索引擎需在0.5秒内返回给用户相应的响应结果,但由于故障,比如部分机房发生断电或断网故障,响应结果增加到了1~2秒。
2、功能上的损失:如在购物高峰时间段,为了保证系统稳定性,部分消费者可能会被引导到一个降级页面
S:软状态
也叫弱状态,允许分布式系统在不同节点的数据副本之间进行数据同步的过程存在延时。指允许系统中的数据存在中间状态,并认为这种中间状态不会影响系统的整体可用性。
E:最终一致性
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态,而不需要实时保证系统数据的强一致性。
最终一致性是一种特殊的弱一致性:系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问都能够获取到最新的值。
总之,BASE理论面向的是大型高可用可扩展的分布式系统,和传统事务的ACID特性是相反的,它完全不同于ACID的强一致性模型,而是提出通过牺牲强一致性来获得可用性,
并允许数据在一段时间内是不一致的,但最终达到一致状态。 在实际的分布式场景中,不同业务单元和组件对数据一致性的要求是不同的,因此在具体的分布式系统架构设计过程中,
ACID特性与BASE理论往往又会结合在一起使用。
分布式一致性协议和算法
为了解决分布式一致性问题,最著名的就是二阶段提交(2PC)协议、三阶段提交(3PC)协议和Paxos算法。
在分布式系统中,每一个机器节点虽然都能够明确知道自己在事务操作过程中的结果是成功或失败,但却无法直接获取到其他分布式节点的操作结果。(如转账,分别调用两个系统一个减钱,一个加钱,如何保证事务的ACID)。因此当一个事务操作需要跨越多个分布式节点的时候,为了保持事务处理的ACID特性,就需要引入第三方“协调者(Coordinator)”(ZK)的组件来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点则被称为“参与者(Participant)”。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正提交。基于这个思想,衍生出了二阶段提交和三阶段提交协议。
二阶段提交,Two-Phase Commit,2PC ,为了使基于分布式系统架构下的所有的节点在进行事务的处理过程中能够保持原子性和一致性而设计的一种方案。
目前绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事物处理的,利用该协议能够非常方便地完成所有分布式事物参与者的协调,统一决定事务的提交或回滚,从而能够有效地保证分布式数据一致性。
过程说明:将事务的提交过程分成了两个阶段来进行处理
阶段一:提交事务请求
1、事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应
2、执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中
3、各参与者向协调者反馈事务询问的响应:如果各参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务,那么就反馈给协调者No响应,
表示事务不可以执行
阶段二:执行事务提交
协调者根据阶段一中各参与者的反馈情况来决定是否可以进行事务提交操作,正常情况下,包含以下两种可能:
1、执行事务提交
假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务提交。
(1)发送提交请求:协调者向所有参与者节点发出Commit请求
(2)事务提交:参与者接受到Commit请求后,会正式执行事务提交操作,并在完成提交后释放整个事务执行期间占用的事务资源
(3)反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack(ackonwledge 确认)消息
(4)完成事务:协调者收到所有参与者反馈的Ack消息后,完成事务(?不会出现提交失败的情况吗)
2、中断事务
假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法结构到所有参与者的反馈响应,那么就会中断事务
(1)发送回滚请求:协调者向所有参与者节点发出Rollback请求
(2)事务回滚:参与者收到Rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放整个事务期间占用的资源
(3)反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发出Ack(ackonwledge 确认)消息
(4)中断事务:协调者收到所有参与者反馈的Ack消息后完成事务中断
所以二阶段提交的核心就是对每个事务采用先尝试后提交的处理方式。
优点:原理简单,实现方便
缺点:同步阻塞、单点问题、脑裂、太过保守。
同步阻塞:在二阶段提交的执行过程中,所有参与该事务操作的逻辑都出于阻塞状态,也就是说,各个参与者在等待其他参与者响应的过程中,将无法进行其他任何操作。
单点问题:协调者出于非常重要的角色,一旦协调者出现问题,特别实在阶段二出现问题,那么其他参与者将会一直出于锁定事务资源的状态中,而无法继续完成事务操作。
数据不一致:如果协调者在向所有的参与者发送Commit请求之后,由于网络问题或者协调者尚未向所有的参与者发送完Commit请求后发生了崩溃,那么导致只有部分参与者收到了Commit
请求,于是出现整个分布式系统数据不一致的情况。
太过保守:事务提交询问过程如果部分参与者故障导致协调者无法获取响应信息的话,协调者只能依靠自身超时机制来判断是否需要中断事务。即没有设计较为完善的容错机制,任何一个
节点的失败都会导致整个事务的失败
三阶段提交,Three-Phase Commit,3PC:在二阶段提交协议的基础上进行了改进。其将二阶段提交协议的阶段一“提交事务请求”过程一分为二,形成了由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。
阶段一:CanCommit
1、事务询问:协调者向所有的参与者发送一个包含事务内容的CanCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应
2、各参与者向协调者反馈事务询问的响应:参与者在接受到协调者的CanCommit请求后,评估其自身能否可以顺利执行事务(此时并没有执行事务,如尝试获取数据库锁),那么反馈Yes响应,
并进入预备处理状态,否则反馈No
阶段二:PreCommit
协调者根据各参与者的反馈情况来决定是否可以进行事务的PreCommit操作,包含两种可能:
1、执行事务预提交
假如协调者从所有的参与者获得的反馈都是Yes,那么就会执行事务预提交
(1)发送预提交请求:协调者向所有的参与者节点发送PreCommit请求
(2)事务预提交:参与者收到PreCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中
(3)各参与者向协调者反馈事务执行的响应:如果各参与者成功执行了事务,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或终止(abort),如果在等待
超时之后仍没有收到协调者的响应,那么各参与者都会执行Commit事务提交。
2、中断事务
假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法结构到所有参与者的反馈响应,那么就会中断事务
(1)发送中断请求:协调者向所有参与者节点发出abort请求
(2)中断事务:无论是收到协调者的abort请求或者在等待协调者请求过程中出现超时,参与者都会中断事务
阶段三:DoCommit
该阶段进行真正的事务提交,存在以下两种可能情况:
1、执行提交
协调者出于正常工作状态,并且收到了来自所有参与者的阶段二的Ack响应
(1)发送提交请求:向所有的参与者发送DoCommit请求
(2)事务提交:参与者收到DoCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源
(3)反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息
(4)完成事务:协调者接受到所有参与者反馈的Ack消息后,完成事务
2、中断事务
协调者出于正常工作状态,并且有任意一个参与者向协调者反馈了No响应,或者在等到超时之后,协调者仍没有收到部分参与者的反馈响应
(1)发送中断请求:协调者向所有的参与者节点发送Abort请求
(2)事务回滚:参与者收到Abort请求后,会利用其在阶段二中的Undo信息执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源
(3)反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息
(4)中断事务:协调者收到所有参与者反馈的Ack消息后,中断事务
需要注意的是,一旦进入阶段三,可能会出现以下两种故障
1、协调者出现问题
2、协调者和参与者之间的网络出现故障
无论出现哪种情况,最终都会导致参与者无法及时接收到来自协调者的DoCommit或者Abort请求,针对这样的情况,参与者都会在等待超时之后,继续执行事务的提交。
优点:相比于二阶段提交,相对减少了同步阻塞范围,能够在协调者出现故障之后继续达成一致(如阶段二的超时都执行提交)
缺点:在参与者收到PreCommit消息后,如果网络出现分区,如果网络出现分区,部分节点无法与协调者进行通信,在超时后仍然后执行事务的提交。若其他节点在这一阶段执行出现问题,协调者发送的是回滚操作,那么就会出现数据不一致的情况。
Paxos算法:是一种基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一
在常见的分布式系统中,总会发生网络分区或节点故障如宕机的情况,Paxos算法就是在发生上述异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,并且保证不论发生以上任何异常,都不会破坏整个系统的一致性。
拜占庭将军问题:在存在消息丢失的不可靠信道上试图通过消息传递的方式达到一致性是不可能的,因此对一致性的研究一般都假设信道是可靠的(消息不会被篡改),或不存在本问题。
Paxos算法需要解决的问题类似如下的故事场景:
在古希腊的一个叫做Paxos的小岛上,岛上采用议会的方式来通过法令,议会中的议员通过信使来进行消息的传递,但是议员和信使都是兼职的,他们可能随时都会离开议会厅(网络分区),并且信使可能会重复的传递消息,也可能会一去不复返(消息丢失);因此议会协议要在这种情况下法令仍然能够正确的产生,并且不会出现冲突。
Paxos算法详解:
一、问题描述:假设有一组可以提出提案的节点集合,那么对一个一致性算法需要保证以下几点:
1、在这些被提出的提案中,只有一个会被选定
2、如果没有提案被提出,那么就不会有提案被选定
3、当一个提案被选定后,节点应该可以获取被选定的提案信息
对于一致性来说,安全性需求有以下几点:
1、只有被提出的提案才能被选定
2、只能有一个值被选定
3、如果某个节点认为某个提案被选定了,那么最终被选定的提案就是这个。
Paxos的目标就是要保证最终有一个提案被选定,当提案被选定后,节点最终也能获取到被选定的提案。
在该一致性算法中,有三种角色,proposer,acceptor和leader,假设不同参与者之间可以通过收发消息来进行通信,那么:
1、每个参与者以任意的速度执行,可能会出错而停止,也可能会重启。同时,即使一个提案被选定后,所有的参与者也都有可能失败或重启,因此除非哪些失败或重启的参与者能够记录某些信息,否则将无法确定最终的值
2、消息在传输过程中可能会出现不可预知的延迟,可有可能重复或丢失,但不会被篡改。
proposer提出提案,提案信息包括提案编号和提议的value;
acceptor收到提案后可以接受(accept)提案,若提案获得多数派(majority)的acceptors的接受,则该提案被批准(chosn);
leaders只能学习被批准的提案。
Paxos算法可以理解成为了实现某个结果不断对行为添加约束而实现的。
算法的约束:
1、决议(value)只有在proposers提出后才能被批准(未被批准的决议称为提案)
2、在一次Paxos算法的执行实例中,只批准(chosn)一个value(获得多数派的接受),可能批准多个提案,但value相同;
3、leaders只能获得被批准(chosn)的value。
作者就是不断加强上面3个约束(主要是第二个)推导了Paxos算法。
推导过程:
若在一个提案被提出的情况下,仍然可以选出一个提案,那么就产生了下面这个约束
P1:一个acceptor必须接受(accept)第一次收到的提案;
注意P1是不完备的,如果恰好一半acceptor接受的提案具有ValueA,另一半提案具有ValueB,那么就无法形成多数派,无法批准任何一个Value。
约束2并不要求只批准一个提案,暗示可能存在多个提案,只要提案的value相同,则不违反约束2。于是产生了下面的约束:
P2:一旦一个具有value V的提案被批准(chosn),那么之后(编号更大的提案)批准(chosn)的提案对应的value必须是value V;
如果P1和P2都能满足,那么约束2就能保证。
批准一个value意味着多个acceptor接受(accept)了该value,因此可对P2进行细化:
P2a:一旦一个具有value V的提案被批准,那么之后任何acceptor再次接受的提案对应的value必须是V;
但是由于通信是异步的,P2a和P1可能会产生冲突。如:一个value被批准后,但此时一个proposer和一个acceptor才从休眠中苏醒,前者提出一个具有新的value的提案。根据P1,后者应当接受,根据P2a,则不应当接受,于是在这种场景下,P2a和P1有矛盾。于是换个思路,转而对Proposer的行为进行约束:
P2b:一旦一个具有Value V的提案被批准,那么之后任何proposer提出的提案对应的值必须是V;
由于acceptor能接受的提案必须由proposer提出,因此约束P2b包含了P2a,是一个更强的约束。
但是根据P2b难以提出实现手段,因此需要进一步细化P2b;假设一个编号为m的value V的提案已经获得批准,显然存在一个acceptors的多数派C,他们都接受了V,又因为其他任何一个多数派和C至少有一个公共成员;为了使对任何编号为n(n>m)的提案对应的Value都为V,则:
P2c:如果一个编号为n的提案对应的value为V,那么存在一个多数派,要么他们中所有人都没有接受编号小于n的任何提案,要么他们已经接受的所有编号小于n的提案中编号最大的那个提案对应的value为V;
用数学归纳法可证明P2c包含P2b(假设具有value V的提案m获得批准,当n=m+1时,采用反证法,假如提案n不具有value V,而是具有value W,根据P2c,则存在一个多数派S1,要么他们中没有人接受过编号小于n的任何提案,要么他们已经接受的所有编号小于n的提案中编号最大的那个提案是value W。由于S1和通过提案m时的多数派C之间至少有一个公共的acceptor,所以以上两个条件都不成立,导出矛盾从而推翻假设,证明了提案n必须具有value V)。
算法的内容
要满足P2c的约束,那么proposer在提出一个提案前,首先要和足以形成多数派的acceptors进行通信,获得他们进行的最近一次接受(accept)的提案(prepare过程),然后决定这次提案的value,形成提案开始投票。当获得多数acceptors接受后,提案获得批准,由acceptor将这个消息告知leader。这就是Paxos算法的内容。
如果一个没有批准过任何proposer提案的acceptor在prepare过程中回答了一个proposer针对提案n的问题,但是在开始对n进行投票前,又接受了一个编号小于n的另一个提案,如果n-1和n具有不同的value,这个投票就违背P2C。因此在prepare过程中,acceptor进行的回答也应包含承诺,不会再接受(accept)编号小于n的提案,这是对P1的细化:
P1a:当且仅当acceptor没有回应过编号大于n的prepare请求时,acceptor接受(accept)编号为n的提案;
完整的算法过程:
阶段一:prepare阶段
1、proposer选择一个提案编号n并将prepare请求发送给acceptors中超过半数的acceptor
2、acceptor收到prepare请求后,如果提案的编号大于它已经回复(代表accept)的所有prepare消息,则acceptor将自己上次接受的提案回复给proposer,并承诺不再接受小于n的提案
阶段二:批准(chosn)阶段
1、当一个proposer收到了超半数acceptors对prepare的回复后,它要向回复prepare请求的acceptors发送accept请求,包括编号n和P2c决定的value(如果根据P2c没有已经接受的value,那么它可以自由决定value)
2、在不违背自己向其他proposer的承诺的前提下,acceptor收到accept请求后即接受这个请求
这个过程在任何时候中断都可以保证正确性。例如如果一个proposer发现已经有其他proposers提出了编号更高的提案,则有必要中断这个过程。因此为了优化,在上述prepare过程中,如果一个acceptor发现存在一个更高编号的提案,则需要通知proposer,提醒其中断这次提案。
Paxos算法的实践:
Google Chubby是一个面向分布式系统的锁服务,通常用于为一个由大量小型计算机构成的松耦合分布式系统提供分布式锁服务。其底层一致性就是以Paxos算法为基础的。