分布式理论

CAP

选项 描述
Consistency(一致性) 指数据在多个副本之间能够保持一致的特性(严格的一致性)
Availability(可用性) 指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应(不保证获取的数据为最新数据)
Partition tolerance(分区容错性) 分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障
CAP原则权衡

通过CAP理论,我们知道无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢。

CA without P

如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,因此CA的系统更多的是允许分区后各子系统依然保持CA。[单机数据库]

CP without A

如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。[数据库强一致集群,eg:Oracle在确认子节点插入成功后才返回]

AP wihtout C

要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。[数据库高可用集群,eg:主备模式,在主节点插入成功后就返回]

互联网系统一般是要求AP然后保持最终一致;金融系统一般是保持CA舍弃P

BASE

选项 描述
基本可用(Basically Available) 什么是基本可用呢?假设系统,出现了不可预知的故障,但还是能用,相比较正常的系统而言:
响应时间上的损失:正常情况下的搜索引擎0.5秒即返回给用户结果,而基本可用的搜索引擎可以在2秒作用返回结果。
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单。但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
软状态(Soft State) 什么是软状态呢?相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种“硬状态”。
软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
最终一致性(Eventually Consistent) 上面说软状态,然后不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性,从而达到数据的最终一致性。这个时间期限取决于网络延时、系统负载、数据复制方案设计等等因素。而在实际工程实践中,最终一致性分为5种:
因果一致性(Causal consistency):如果节点A在更新完某个数据后通知了节点B,那么节点B之后对该数据的访问和修改都是基于A更新后的值。于此同时,和节点A无因果关系的节点C的数据访问则没有这样的限制。
读己之所写(Read your writes):节点A更新一个数据后,它自身总是能访问到自身更新过的最新值,而不会看到旧值。其实也算一种因果一致性。
会话一致性(Session consistency)会话一致性将对系统数据的访问过程框定在了一个会话当中:系统能保证在同一个有效的会话中实现 “读己之所写” 的一致性,也就是说,执行更新操作之后,客户端能够在同一个会话中始终读取到该数据项的最新值。
单调读一致性(Monotonic read consistency):如果一个节点从系统中读取出一个数据项的某个值后,那么系统对于该节点后续的任何数据访问都不应该返回更旧的值。
单调写一致性(Monotonic write consistency):一个系统要能够保证来自同一个节点的写操作被顺序的执行。

由于BASE理论需要在一致性和可用性方面做出权衡,因此涌现了很多关于一致性的算法和协议。其中比较著名的有二阶提交协议(2 Phase Commitment Protocol),三阶提交协议(3 Phase Commitment Protocol)和Paxos算法

二阶提交协议和三阶提交协议就是基于XA规范提出的其中,二阶段提交就是实现XA分布式事务的关键。

XA规范

XA规范是由 X/Open组织(即现在的 Open Group )定义的分布式事务处理模型。 X/Open DTP 模型( 1994 )包括

  • 应用程序( AP )
  • 事务管理器( TM ):交易中间件等
  • 资源管理器( RM ):关系型数据库等
  • 通信资源管理器( CRM ):消息中间件等
二阶段提交
  • 准备阶段

事务询问:协调者向所有的参与者询问,是否准备好了执行事务,并开始等待各参与者的响应。
执行事务:各参与者节点执行事务操作。如果本地事务成功,将Undo和Redo信息记入事务日志中,但不提交;否则,直接返回失败,退出执行。
各参与者向协调者反馈事务询问的响应:如果参与者成功执行了事务操作,那么就反馈给协调者 Yes响应,表示事务可以执行提交;如果参与者没有成功执行事务,就返回No给协调者,表示事务不可以执行提交。

  • 提交阶段

提交事务过程如下
发送提交请求:协调者向所有参与者发出commit请求。
事务提交:参与者收到commit请求后,会正式执行事务提交操作,并在完成提交之后,释放整个事务执行期间占用的事务资源。
反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack信息。
事务提交确认:协调者接收到所有参与者反馈的Ack信息后,完成事务。

中断事务过程如下
发送回滚请求:协调者向所有参与者发出Rollback请求。
事务回滚:参与者接收到Rollback请求后,会利用其在提交阶段种记录的Undo信息,来执行事务回滚操作。在完成回滚之后,释放在整个事务执行期间占用的资源。
反馈事务回滚结果:参与者在完成事务回滚之后,想协调者发送Ack信息。
事务中断确认:协调者接收到所有参与者反馈的Ack信息后,完成事务中断。

二阶段提交优缺点
优点 缺点
原理简单,实现方便 同步阻塞:在二阶段提交的过程中,所有的节点都在等待其他节点的响应,无法进行其他操作。这种同步阻塞极大的限制了分布式系统的性能。
单点问题:协调者在整个二阶段提交过程中很重要,如果协调者在提交阶段出现问题,那么整个流程将无法运转。更重要的是,其他参与者将会处于一直锁定事务资源的状态中,而无法继续完成事务操作。
数据不一致:假设当协调者向所有的参与者发送commit请求之后,发生了局部网络异常,或者是协调者在尚未发送完所有 commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了commit请求。这将导致严重的数据不一致问题。
容错性不好:如果在二阶段提交的提交询问阶段中,参与者出现故障,导致协调者始终无法获取到所有参与者的确认信息,这时协调者只能依靠其自身的超时机制,判断是否需要中断事务。显然,这种策略过于保守。换句话说,二阶段提交协议没有设计较为完善的容错机制,任意一个节点是失败都会导致整个事务的失败。
开源实现

Raincat

三阶段提交

三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase commit protocol),是二阶段提交(2PC)的改进版本。
所谓的三个阶段分别是:询问,然后再锁资源,最后真正提交。

  • 第一阶段:CanCommit

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。
事务询问:协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应
响应反馈:参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态;否则反馈No。

  • 第二阶段:PreCommit
    协调者在得到所有参与者的响应之后,会根据结果执行2种操作:执行事务预提交,或者中断事务

执行事务预提交
发送预提交请求:协调者向所有参与者节点发出 preCommit 的请求,并进入 prepared 状态。
事务预提交:参与者受到 preCommit 请求后,会执行事务操作,对应 2PC 准备阶段中的 “执行事务”,也会 Undo 和 Redo 信息记录到事务日志中。
各参与者响应反馈:如果参与者成功执行了事务,就反馈 ACK 响应,同时等待指令:提交(commit) 或终止(abort)

中断事务
发送中断请求:协调者向所有参与者节点发出 abort 请求 。
中断事务:参与者如果收到 abort 请求或者超时了,都会中断事务。

  • 第三阶段:Do Commit
    该阶段进行真正的事务提交,也可以分为以下两种情况

执行提交
发送提交请求:协调者接收到各参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
事务提交:参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
响应反馈:事务提交完之后,向协调者发送 ACK 响应。
完成事务:协调者接收到所有参与者的 ACK 响应之后,完成事务。

中断事务
协调者没有接收到参与者发送的 ACK 响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。
发送中断请求:协调者向所有参与者发送 abort 请求。
事务回滚:参与者接收到 abort 请求之后,利用其在阶段二记录的 undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
反馈结果:参与者完成事务回滚之后,向协调者发送 ACK 消息。
中断事务:协调者接收到参与者反馈的 ACK 消息之后,完成事务的中断。

三阶段提交优缺点
优点 缺点
相对于二阶段提交,三阶段提交主要解决的单点故障问题,并减少了阻塞的时间。因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行 commit。而不会一直持有事务资源并处于阻塞状态。 三阶段提交也会导致数据一致性问题。由于网络原因,协调者发送的 abort 响应没有及时被参与者接收到,那么参与者在等待超时之后执行了 commit 操作。这样就和其他接到 abort 命令并执行回滚的参与者之间存在数据不一致的情况。
开源实现

hmily

Paxos协议

Paxos协议中,有一组完全对等的参与节点(称为accpetor),这组节点各自就某一事件做出决议,如果某个决议获得了超过半数节点的同意则生效。Paxos协议中只要有超过一半的节点正常,就可以工作,能很好对抗宕机、网络分化等异常情况。

Paxos协议中,有三类节点:

  • Proposer:提案者。Proposer可以有多个,Proposer提出议案(value)。所谓value,在工程中可以是任何操作,例如“修改某个变量的值为某个值”、“设置当前primary为某个节点”等等。Paxos协议中统一将这些操作抽象为value。不同的Proposer可以提出不同的甚至矛盾的value,例如某个Proposer提议“将变量X设置为1”,另一个Proposer提议“将变量X设置为2”,但对同一轮Paxos过程,最多只有一个value被批准。

  • Acceptor:批准者。Acceptor有N个,Proposer提出的value必须获得超过半数(N/2+1)的Acceptor批准后才能通过。Acceptor之间完全对等独立。

  • Learner:学习者。Learner学习被批准的value。所谓学习就是通过读取各个Proposer对value的选择结果,如果某个value被超过半数Acceptor通过,则Learner学习到了这个value。这里类似Quorum机制,某个value需要获得W=N/2+1的Acceptor批准,从而学习者需要至少读取N/2+1个Accpetor,至多读取N个Acceptor的结果后,能学习到一个通过的value。

上述三类角色只是逻辑上的划分,实践中一个节点可以同时充当这三类角色。

Paxos协议一轮一轮的进行,每轮都有一个编号。每轮Paxos协议可能会批准一个value,也可能无法批准一个value。如果某一轮Paxos协议批准了某个value,则以后各轮Paxos只能批准这个value。上述各轮协议流程组成了一个Paxos协议实例,即一次Paxos协议实例只能批准一个value,这也是Paxos协议强一致性的重要体现。

每轮Paxos协议分为两个阶段,准备阶段和批准阶段,在这两个阶段Proposer和Acceptor有各自的处理流程。

  • 1 准备阶段(占坑阶段)
    • 1.1 第一阶段A:Proposer选择一个提议编号n,向所有的Acceptor广播Prepare(n)请求。
    • 1.2 第一阶段B:Acceptor接收到Prepare(n)请求,若提议编号n比之前接收的Prepare请求都要大,则承诺将不会接收提议编号比n小的提议,并且带上之前Accept的提议中编号小于n的最大的提议,否则不予理会。
  • 2 接受阶段(提交阶段)
    • 2.1 第二阶段A:整个协议最为关键的点:Proposer得到了Acceptor响应
      • 2.1.1 如果未超过半数accpetor响应,直接转为提议失败。
      • 2.1.2 如果超过多数Acceptor的承诺,又分为不同情况:
        • 2.1.2.1 如果所有Acceptor都未接收过值(都为null),那么向所有的Acceptor发起自己的值和提议编号n,记住,一定是所有Acceptor都没接受过值;
        • 2.1.2.2 如果有部分Acceptor接收过值,那么从所有接受过的值中选择对应的提议编号最大的作为提议的值,提议编号仍然为n。但此时Proposer就不能提议自己的值,只能信任Acceptor通过的值,维护一但获得确定性取值就不能更改原则
    • 2.2 第二阶段B Acceptor接收到提议后,如果该提议版本号不等于自身保存记录的版本号(第一阶段记录的),不接受该请求,相等则写入本地

把握和理解这两点就基本理解了paxos的精髓
1.理解第一阶段accpetor的处理流程:如果本地已经写入了,不再接受和同意后面的所有请求,并返回本地写入的值;如果本地未写入,则本地记录该请求的版本号,并不再接受其他版本号的请求,简单来说只信任最后一次提交的版本号的请求,使其他版本号写入失效
2.理解第二阶段proposer的处理流程:未超过半数accpetor响应,提议失败;超过半数的accpetor值都为空才提交自身要写入的值,否则选择非空值里版本号最大的值提交,最大的区别在于是提交的值是自身的还是使用以前提交的

Paxos能解决的问题
  • database replication, log replication等, 如bdb的数据复制就是使用paxos兼容的算法。Paxos最大的用途就是保持多个节点数据的一致性

  • naming service, 如大型系统内部通常存在多个接口服务相互调用。

    • 1 通常的实现是将服务的ip/hostname写死在配置中,当service发生故障时候,通过手工更改配置文件或者修改DNS指向的方法来解决。缺点是可维护性差,内部的单元越多,故障率越大。
    • 2 LVS双机冗余的方式,缺点是所有单元需要双倍的资源投入。
      通过Paxos算法来管理所有的naming服务,则可保证high available分配可用的service给client。象ZooKeeper还提供watch功能,即watch的对象发生了改变会自动发notification, 这样所有的client就可以使用一致的,高可用的接口。
  • config配置管理

    • 1 通常手工修改配置文件的方法,这样容易出错,也需要人工干预才能生效,所以节点的状态无法同时达到一致。
    • 2 大规模的应用都会实现自己的配置服务,比如用http web服务来实现配置中心化。它的缺点是更新后所有client无法立即得知,各节点加载的顺序无法保证,造成系统中的配置不是同一状态
  • membership用户角色/access control list, 比如在权限设置中,用户一旦设置某项权限比如由管理员变成普通身份,这时应在所有的服务器上所有远程CDN立即生效,否则就会导致不能接受的后果。

  • 号码分配。通常简单的解决方法是用数据库自增ID, 这导致数据库切分困难,或程序生成GUID, 这通常导致ID过长。更优雅的做法是利用paxos算法在多台replicas之间选择一个作为master, 通过master来分配号码。当master发生故障时,再用paxos选择另外一个master。

Paxos协议实现

微信PaxosStore:深入浅出Paxos算法协议
微信自研生产级paxos类库PhxPaxos实现原理介绍
phxpaxos

小结
协议 特点
2PC 强一致性保障
准备阶段完成资源操作
如果准备过程中出现问题,可以回滚
提交阶段不允许出错
资源层面提供保障业务侵入性低
协议成本高(依赖于资源管理器对分布式事务的支持),并且存在全局锁
TCC 将对不同的DB访问、不同的业务操作通过TCC模型协调为一个原子操作,解决了分布式应用架构场景下的事务问题
TCC 模型通过 2PC 原子提交协议保证分布式事务的的原子性,把资源层的隔离性上升到业务层,交给业务逻辑来实现,规避了资源层在 2PC 和 2PL 下对资源占用导致的性能低下问题
TCC 模型需要拆分业务逻辑成两个阶段,并实现 Try、Confirm、Cancel 三个接口,定制化程度高,开发成本高

你可能感兴趣的:(分布式理论)