分布式理论(CAP BASE TCC 2PC 3PC)

分布式数据一致性

因为在分布式系统中,为了保证数据的高可用,会将数据保留多个副本(replica),这些副本分布在不同的物理机器上。所以可能导致数据在进行写入副本的过程中出现丢失等故障,造成数据不一致的问题。

CAP

Consistency 一致性Availability 可用性Partition Tolerance 分区容错性

Consistency 一致性:这个和数据库ACID的一致性类似,但这里关注的所有数据节点上的数据一致性和正确性,而数据库的ACID关注的是在在一个事务内,对数据的一些约束。系统在执行过某项操作后仍然处于一致的状态。也就是说,在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新写入的值

Availability 可用性:每一个操作总是能够在一定时间内返回结果。所以系统操作成功or失败的结果一定要在给定的时间内返回。但也就是每一次请求都能得到一个及时的、非错的响应,但是不保证请求的结果是基于最新写入的数据。

Partition Tolerance:分区容错性。是否能够对数据进行分区,由于节点之间的网络问题,即使一些消息丢包或者延迟,整个系统依然能继续提供服务。考虑到性能和可伸缩性。
在这里插入图片描述
强一致性:当更新操作完成之后,任何多个后续进程or线程的访问都会返回最新的更新过的值。 牺牲了可用性。

弱一致性:不保证后续进程or线程返回最新的更新过的值。用户读到某一个操作对系统特定数据的更新需要一段时间,该段时间称为**“不一致性窗口”**。也就是说,系统在数据写入成功之后,不承诺立即可以读取到最新写入的数据,也不会具体的承诺多久之后可以读到。

最终一致性:弱一致性的一种特例。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要是受到通信延迟,系统负载和复制副本的个数影响。
DNS是一个典型的最终一致性系统。

一致性和可用性的抉择

由于CAP 理论中,三点只能实现其中两点,但是由于网络硬件肯定会出现或多或少的延迟丢包的问题,所以分区容忍性是我们必须要实现的。所以我们只能在一致性和可用性中进行抉择。
对于web2.0网站来说,关系数据库的很多主要特性却往往无用武之地

  1. 数据库事务一致性需求
      很多web实时系统并不要求严格的数据库事务,对读一致性的要求很低,有些场合对写一致性要求并不高。允许实现最终一致性。
  2. 数据库的写实时性和读实时性需求
      对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多web应用来说,并不要求这么高的实时性,比方说发一条消息之 后,过几秒乃至十几秒之后,我的订阅者才看到这条动态是完全可以接受的。
  3. 对复杂的SQL查询,特别是多表关联查询的需求
      任何大数据量的web系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的报表查询,特别是SNS类型的网站,从需求以及产品设计角 度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL的功能被极大的弱化了。

BASE理论

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

1. 基本可用

也就是说分布式系统在出现不可预知故障的时候,允许损失部分可用性。
这绝不等价于系统不可用。比如:

(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒

(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面

2.软状态

软状态指的是允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性 。也就说允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

3. 最终一致性

最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

总的来说,BASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。
但同时,在实际的分布式场景中,不同业务单元和组件对数据一致性的要求是不同的,因此在具体的分布式系统架构设计过程中,ACID特性和BASE理论往往又会结合在一起。

补偿事务 TCC

TCC的核心思想是针对每一个操作,都要注册一个与其对应的确认和补偿(撤销)操作。

  1. Try 阶段是对业务系统做检测以及资源预留;
  2. Confirm阶段主要是对业务系统做出确认提交;try阶段成功,默认confirm一定成功。
  3. Cancel阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源得到释放。
    举个例子,假入 Bob 要向 Smith 转账,思路大概是:
    我们有一个本地方法,里面依次调用
    1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
    2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
    3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

两阶段提交 2PC

两阶段提交协议是协调所有分布式原子事务参与者,并决定提交或者是取消(回滚)的分布式算法。

有两类机器(节点):一类是协调者Coordinator,一类是事务参与者(participants、cohorts或者workers)。事务参与者可以看做是数据副本。

协议中保证:1、 假设每一个节点都会记录写前日志并持久化存储,即使节点发生故障日志也不会丢失。
2、假设节点不会发生永久性故障而且任意两个节点都可以互相通信。

两个阶段

在这里插入图片描述

  1. 请求阶段(表决阶段 commit-request phase\\ volting phase)

协调者将通知事务参与者 准备提交or取消事务,然后进入表决过程;
表决过程中,参与者将告知协调者自己的决策:同意or取消。同意则表示事务参与者本地作业执行成功;取消表示本地作业执行故障。

  1. 提交阶段(commit phase)

coordinator将基于第一个阶段投票结果进行决策:提交or取消。
当且仅当所有的参与者都同意的时候才会通知all参与者提交事务,否则coordinator将通知all participants取消事务。

参与者接收到协调者发来的消息之后执行相应的操作。

两阶段提交的缺点
  1. 同步阻塞问题,执行过程中所有参与节点都是事务阻塞型的。有第三方占有公共资源的时候,该第三方节点访问公共资源不得不处于阻塞状态;
  2. 单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
  3. 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。
    而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。

so,如果协调者出错同时参与者也出错时,两阶段无法保证事务执行的完整性。
考虑协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。
那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

三阶段提交协议

三阶段提交协议在coordinator和participants中都引入了超时机制。并且将两阶段提交协议的第一个阶段拆分成了两步。询问,然后再锁资源,最后真正提交。
在这里插入图片描述

三个阶段的执行
  1. CanCommit阶段
    3PC的CanCommit阶段很像2PC中的准备阶段。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应。否则就返回No。
  2. PreCommit阶段
    在此阶段,coordinator根据participants的响应情况来决定是否可以继续事务的PreCommit操作。
    可能有以下几种情况:
    A. all participants都是yes,那么就进行事务的预执行:
  • 发送预提交请求。Coordinator向Cohort发送PreCommit请求,并进入Prepared阶段
  • 事务预提交。Cohort接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。
  • 响应反馈。若Cohort成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

B. 其中一个Cohort向coordinator发送了No响应,或者等待超时之后,Coordinator都没有接收到Cohort的响应,那么就中断事务。

  • 发送中断请求。Coordinator向所有的cohort发送abort请求
  • 中断事务。cohort收到来自coordinator的abort请求之后(或者超时之后仍未收到Cohort的请求),执行事务的中断。
  1. DoCommit阶段
    此阶段进行真正的事务提交。分为以下两种情况。
    ####### 1. 执行提交 #######
  • 发送提交请求。Coordinator接收到cohort发送的ACK响应,他将从预提交状态进入提交状态。并向所有的Cohort发送doCommit请求
  • 事务提交。cohort接收到doCommit请求之后,执行正式的事务提交,并在完成事务提交之后释放所有事务资源。
  • 响应反馈。事务提交完之后,向coordinator发送ACK响应
  • 完成事务。coordinator接收到所有的cohort的ack响应之后,完成事务。
    ######### 2. 中断事务 #######
    coordinator没有接收到cohort发送的ack响应(可能cohort发送的不是ack响应,也可能是响应超时 )那么就会执行中断事务。

三阶段提交和两阶段提交协议的不同

2PC和3PC的不同

  1. 3PC中,对于双方(coordinator和cohort)都加入了超时机制。(2PC中只有协调者拥有超时机制,就是说在一定时间内没有收到cohort的消息则默认失败)

  2. 3PC中,是在2PC的准备阶段和提交阶段之间,插入预提交阶段,使得3PC拥有CanCommit、PreCommit、DoCommit三个阶段。

PreCommit是一个缓冲,保证了在最后提交阶段之前各个参与节点的状态时一致的。

转一个维基百科的例子:

三阶段提交是“非阻塞”协议。
三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个准备阶段,
使得原先在两阶段提交中,参与者在投票之后,由于协调者发生崩溃或错误,
而导致参与者处于无法知晓是否提交或者中止的“不确定状态”所产生的可能相当长的延时的问题得以解决。 举例来说,假设有一个决策小组由一个主持人负责与多位组员以电话联络方式协调是否通过一个提案,以两阶段提交来说,主持人收到一个提案请求,打电话跟每个组员询问是否通过并统计回复,然后将最后决定打电话通知各组员。
要是主持人在跟第一位组员通完电话后失忆,而第一位组员在得知结果并执行后老人痴呆,那么即使重新选出主持人,也没人知道最后的提案决定是什么,也许是通过,也许是驳回,不管大家选择哪一种决定,都有可能与第一位组员已执行过的真实决定不一致,老板就会不开心认为决策小组沟通有问题而解雇。
三阶段提交即是引入了另一个步骤,主持人打电话跟组员通知请准备通过提案,以避免没人知道真实决定而造成决定不一致的失业危机。
为什么能够解决二阶段提交的问题呢?
回到刚刚提到的状况,在主持人通知完第一位组员请准备通过后两人意外失忆,即使没人知道全体在第一阶段的决定为何,全体决策组员仍可以重新协调过程或直接否决,不会有不一致决定而失业。
那么当主持人通知完全体组员请准备通过并得到大家的再次确定后进入第三阶段,
当主持人通知第一位组员请通过提案后两人意外失忆,这时候其他组员再重新选出主持人后,
仍可以知道目前至少是处于准备通过提案阶段,表示第一阶段大家都已经决定要通过了,此时便可以直接通过。

在这里插入图片描述

三阶段提交协议的缺点

如果进入PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort收到并进行了abort操作,
而其他对于系统状态未知的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。

**优点:**降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。

**缺陷:**脑裂问题依然存在,即在参与者收到PreCommit请求后等待最终指令,如果此时协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

本地消息表(异步确保)

本地消息表核心思想是将分布式事务拆成本地事务进行处理,思路来源于ebay。
在这里插入图片描述
基本思路:分为消息生产方、消息消费方、本地消息表。
消息生产方:额外新建一个消息表,并记录消息发送的状态。消息表和业务数据要在一个事务中提交(就是说消息表和事务在同一个数据库中),然后消息经过MQ之后发送到消息的消费方。若消息发送失败,会进行重新发送

消息消费方:处理该消息,并完成自己的业务逻辑。如果本地事务处理成功,表明已经处理成功,若处理失败,则重新执行。若是业务上的失败,可以给生产者发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息 or 失败的消息再发送一遍。
如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。

缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
还有其他的几种分布式的实现方案见:(包括2PC TCC 本地消息表 MQ事务消息 Sagas 事务模型等)https://blog.csdn.net/hxpjava1/article/details/79409395

分布式系统的典型应用

分布式系统是一个非常广泛的概念,它最终要落实到解决实际问题上,不同的问题有不同的方法和架构。所有的开源软件都是以某个应用场景出现,而纯粹以“分布式”概念进行划分的比较少见。
但如果以算法划分,到能分出几类:

  1. Leader选举为主的一类算法,比如paxos、viewstamp,就是现在zookeeper、Chuby等工具的主体
  2. 以分布式事务为主的一类主要是二段提交,这些分布式数据库管理器及数据库都支持
  3. 以若一致性为主的,主要代表是Cassandra的W、R、N可调节的一致性
  4. 租赁机制为主的,主要是一些分布式锁的概念,目前还没有看到纯粹“分布式”锁的实现
  5. 以失败探测为主的,主要是Gossip和phi失败探测算法,当然也包括简单的心跳
  6. 以弱一致性、因果一致性、顺序一致性为主的,开源尚不多,但大都应用在Linkedin、Twitter、Facebook等公司内部
  7. 当然以异步解耦为主的,还有各类Queue
    资料:https://www.cnblogs.com/AndyAo/p/8228099.html
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值