从Paxos到Zookeeper 读书笔记(2)-分布式的CAP理论

1 分布式环境的各种问题

1.1 通信异常

  • 消息丢失
分布式系统需要各个节点之间进行网络通信,因此每次网络通信都会伴随网络不可用的风险,如路由器、光纤等硬件设备或者是系统不可用,都会导致网络不可用。
  • 消息延迟
即使分布式系统各节点网络的通信能正常进行,但网络通信的延迟是不可忽略的因素。

1.2 网络分区

当网络发生异常时,导致部分节点网络延迟不断增大,最终导致只有分布式系统的少数节点能正常通信-----这种现象称为网络分区,也称“脑裂”。

当出现网络分区时,会出现局部小集群,在极端情况下,这些小集群会独立完成原本整个分布式系统才能完成的功能,这就对分布式一致性提出了巨大挑战。

1.3 三态

分布式系统的每一次请求或响应,存在特有的“三态”:成功、失败和超时。

由于网络是不可靠的,在绝大多数情况下,网络通信能收到成功与失败的响应,但当网络出现异常时,就可能出现超时现象,此时无法确定当前请求是否被成功处理。

1.4 节点故障

节点故障又是分布式环境下的一个常见问题,每个节点都可能出现宕机或“僵死”的现象。

2 分布式事务

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务会涉及多个数据源或业务系统的操作。

一个典型分布式事务场景:一个转账操作涉及两个异地的银行服务,一个是本地服务的取款服务,另一个是目标银行提供的存款服务,这两个服务本身是无状态并且是相互独立的,共同构成一个分布式事务。如果一个取款成功,一个失败,那么该分布式事务必须回滚。

一个分布式事务可以看作是一系列子事务(就是一系列分布式操作序列)。因此,分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务特性。但由于在分布式事务中,各个子事务的执行是分布式的,因此要实现种能够保证ACID特性的分布式事务处理系统就显得格外复杂。

2.1 CAP理论

CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C: Consistency)、 可用性 .(A: Availab山ty)和分区容错性(P:Partition tolerance)这三个基本需求, 最多只能同时满足其中的两项。

2.1.1 一致性

在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性。(与ACID的C不是同一个概念) 在一致性的需求下, 当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
对于一个将数据副本分布在不同分布式节点上的系统来说,如果对第一个节点的数据进行了更新操作并且更新成功后, 却没有使得第二个节点上的数据得到相应的更新, 于是在对第二个节点的数据进行读取操作时, 获取的依然是老数据(或称为脏数据), 这就是典型的分布式数据不一致情况。 在分布式系统中, 如果能够做到针对一个数据项的更新操作执行成功后, 所有的用户都可以读取到其最新的值,那么这样的系统就被认为具有强一致性(或严格的一致性)。

2.1.2 可用性

可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 

2.1.3 分区容错性

分区容错性约束了一个分布式系统需要具有如下特性:分布式系统在遇到任何网络分区故障的时候, 仍然需要能够保证对外提供满足一致性和可用性的服务, 除非是整个网络环境都发生了故障。

对于一个分布式系统而言,分区容错性P是一个最基本的要求。因为在分布式系统中,网络问题是一个必然要面临的问题,因此分区容错性是分布式系统必须面对的问题。因此P是必须的,设计师往往是要在A和C之间做一个trade-off,C和A并不是非此即彼的。


以下转载:http://mp.weixin.qq.com/s/VbFOscil4s4XEbhznisvhQ

2.2 BASE理论

CAP理论告诉我们一个悲惨但不得不接受的事实——我们只能在C、A、P中选择两个条件。而对于业务系统而言,我们往往选择牺牲一致性来换取系统的可用性和分区容错性。不过这里要指出的是,所谓的“牺牲一致性”并不是完全放弃数据一致性,而是牺牲强一致性换取弱一致性。下面来介绍下BASE理论。

  • BA:Basic Available 基本可用

    • “一定时间”可以适当延长
      当举行大促时,响应时间可以适当延长

    • 给部分用户返回一个降级页面
      给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。

    • 整个系统在某些不可抗力的情况下,仍然能够保证“可用性”,即一定时间内仍然能够返回一个明确的结果。只不过“基本可用”和“高可用”的区别是:

  • S:Soft State:柔性状态
    同一数据的不同副本的状态,可以不需要实时一致。

  • E:Eventual Consisstency:最终一致性
    同一数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定时间后仍然是一致的。

2.3 酸碱平衡

ACID能够保证事务的强一致性,即数据是实时一致的。这在本地事务中是没有问题的,在分布式事务中,强一致性会极大影响分布式系统的性能,因此分布式系统中遵循BASE理论即可。但分布式系统的不同业务场景对一致性的要求也不同。如交易场景下,就要求强一致性,此时就需要遵循ACID理论,而在注册成功后发送短信验证码等场景下,并不需要实时一致,因此遵循BASE理论即可。因此要根据具体业务场景,在ACID和BASE之间寻求平衡。

2.4 分布式事务协议

下面介绍几种实现分布式事务的协议。

2.4.1 两阶段提交协议 2PC

分布式系统的一个难点是如何保证架构下多个节点在进行事务性操作的时候保持一致性。为实现这个目的,二阶段提交算法的成立基于以下假设:

  • 该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts)。且节点之间可以进行网络通信。

  • 所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。

  • 所有节点不会永久性损坏,即使损坏后仍然可以恢复。

1. 第一阶段(投票阶段):

  1. 协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。

  2. 参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)

  3. 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。

2. 第二阶段(提交执行阶段):

当协调者节点从所有参与者节点获得的相应消息都为"同意"时:

  1. 协调者节点向所有参与者节点发出"正式提交(commit)"的请求。

  2. 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。

  3. 参与者节点向协调者节点发送"完成"消息。

  4. 协调者节点受到所有参与者节点反馈的"完成"消息后,完成事务。

如果任一参与者节点在第一阶段返回的响应消息为"中止",或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

  1. 协调者节点向所有参与者节点发出"回滚操作(rollback)"的请求。

  2. 参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。

  3. 参与者节点向协调者节点发送"回滚完成"消息。

  4. 协调者节点受到所有参与者节点反馈的"回滚完成"消息后,取消事务。

不管最后结果如何,第二阶段都会结束当前事务。

二阶段提交看起来确实能够提供原子性的操作,但是不幸的事,二阶段提交还是有几个缺点的:

  1. 执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。

  2. 参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。(没有多少容错机制)

  3. 协调者发生故障。参与者会一直阻塞下去。需要额外的备机进行容错。(这个可以依赖后面要讲的Paxos协议实现HA)

  4. 二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

为此,Dale Skeen和Michael Stonebraker在“A Formal Model of Crash Recovery in a Distributed System”中提出了三阶段提交协议(3PC)。

2.4.2 三阶段提交协议 3PC

与两阶段提交不同的是,三阶段提交有两个改动点。

  • 引入超时机制。同时在协调者和参与者中都引入超时机制。

  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。

1. CanCommit阶段

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

  1. 事务询问
    协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。

  2. 响应反馈
    参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No

2. PreCommit阶段

协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。
假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。

  1. 发送预提交请求
    协调者向参与者发送PreCommit请求,并进入Prepared阶段。

  2. 事务预提交
    参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。

  3. 响应反馈  
    如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。

  1. 发送中断请求 
    协调者向所有参与者发送abort请求。

  2. 中断事务  
    参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。

3. doCommit阶段
该阶段进行真正的事务提交,也可以分为以下两种情况。

该阶段进行真正的事务提交,也可以分为以下两种情况。

3.1 执行提交

  1. 发送提交请求
    协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。

  2. 事务提交    
    参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。

  3. 响应反馈 
    事务提交完之后,向协调者发送Ack响应。

  4. 完成事务 
    协调者接收到所有参与者的ack响应之后,完成事务。

3.2 中断事务 
协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。

  1. 发送中断请求 
    协调者向所有参与者发送abort请求

  2. 事务回滚 
    参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。

  3. 反馈结果 
    参与者完成事务回滚之后,向协调者发送ACK消息

  4. 中断事务 
    协调者接收到参与者反馈的ACK消息之后,执行事务的中断。



猜你喜欢

转载自blog.csdn.net/u013885699/article/details/79649945