分布式一致性那些事儿

1 什么是微服务、微服务架构、分布式

  • 微服务:它是一个单一的、完整的服务,这个服务只作部分功能。甚至一个微服务只负责建立数据、一个微服务只负责修改数据,也是可用的。不一样的微服务之间经过rpc相互交互。一个微服务是一个完整的服务,它的开发、联调、测试、部署能够由一个团队负责,与其余服务没有任何关系。
  • 微服务架构:当拿到一个服务的时候,从它的用户量、功能复杂度、并发性等角度考量,决定是否须要拆分红多个微服务。若是用户量不多,或者功能比较简单,或者并发性很低,则能够考虑只用一个服务实现全部功能;若是用户量不少,或者功能比较复杂,或者并发性很高,则能够考虑按功能模块划分红多个微服务,每一个微服务实现某一个模块的功能。这就是微服务架构
  • 分布式:一个系统拆分红了多个微服务,而且部署到不一样的逻辑服务器或者虚拟机,则称之为分布式部署。分布式部署的应用不必定是微服务架构,好比集群部署,它是把相同的服务部署到不一样的机器上,功能上仍属于单体应用。所以微服务架构必定是多个功能不一样的微服务而且部署在不一样的逻辑服务器或者虚拟机上。

2 分布式的优势

传统单体服务 分布式微服务
新功能开发 复杂 工做量少、容易实现
架构设计 难度小 难度大
系统性能 相应时间快,吞吐量小 相应时间慢、吞吐量大
系统运维 运维简单 运维复杂
技术 技术单一且封闭 技术多样且开放
测试和查错 简单 复杂
扩展性 扩展性不好 扩展性很是好

3 分布式带来的问题

分布式提升了系统可用性、吞吐量,加强了系统扩展性,但同时也带来了一些问题。好比增长了系统复杂性、排查问题的难度、服务之间调度产生的各类问题,最大的问题就是数据一致性。
某一件事情分配给了多个不一样的服务取实现,因为网络传输问题,只要有任何一个服务执行失败则整件事情就失败了,其余已经执行成功的服务就产生了脏数据,形成了数据不一致。这是分布式带来的、不可避免的问题。业界对这个问题作了许多研究,提出了多种解决方案。面试

4 CAP理论

CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。
算法

4.1 CAP原则简介

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

什么是分区?数据库

在分布式系统中,不一样的节点分布在不一样的子网络中,因为一些特殊的缘由,这些子节点之间出现了网络不通的状态,但他们的内部子网络是正常的。从而致使了整个系统的环境被切分红了若干个孤立的区域,同时每一个孤立的区域可以对外提供完整的功能,这就是分区。服务器

4.2 CAP原则的权衡

经过CAP理论,咱们知道没法同时知足一致性、可用性和分区容错性这三个特性,那要舍弃哪一个呢?markdown

(1) C A without P

若是不要求P(不容许分区),即单一系统或者全部微服务都在一个网络内,好比一台逻辑服务器或者虚拟机少给你,则C(强一致性)和A(可用性)是能够保证的。好比单一数据库、单一服务。可是分布式系统分区必定存在分区,所以P必定须要考虑。因此CAP理论实际上是容许P以后,考虑如何尽最大努力保证C和A。网络

(2) C P without A

若是不要求A(可用),至关于每一个请求都须要在Server之间强一致,而P(分区)会致使同步时间无限延长,如此CP也是能够保证的。不少传统的数据库分布式事务都属于这种模式,当发生C或者P的时候,系统不可用,必须强制确保C和P。架构

(3) A P without C

要高可用并容许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每一个节点只能用本地数据提供服务,而这样会致使全局数据的不一致性,可是整个系统对外展现仍是可用的,若是测试有数据修改则更会形成数据不一直,所以这个时候可用对系统作一些控制,好比容许只读,不容许写数据,至关于下降了部分A的功能,来确保数据不一致程度最小。还有一种方法就是在各个分区内记录下写的操做,等P回复以后再同步数据,确保数据最终一致性,可是这样有风险。并发

上面三种权衡策略有各自的优缺点,须要根据不一样的场景选择不一样的权衡策略。好比对于普通的、影响不大的应用,可用选择A P without C,舍弃数据一致性来确保系统可用;可是对于金融、支付系统必须强制确保C,当发生了P的时候,可用舍弃A,即宁肯系统不可用,也要保证数据强一致性。
CAP理论之间的关系以下图:
1640df3c315644fc (1).jpg
ACID就是C&A without P, 对于单一数据库,没有P,所以单一数据库可用保证ACID特性。
BASE就是牺牲了数据强一致性,保证了A&P,但它能确保数据最终一致性。
运维

5 BASE理论

BASE理论是Basically Available(基本可用),Soft State(软状态)和Eventually Consistent(最终一致性)三个短语的缩写。
分布式

核心思想

既是没法作到强一致性(Strong consistency),但每一个应用均可以根据自身的业务特色,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

5.1 基本可用(Basically Available)

什么是基本可用呢?就是发生了P,牺牲C,还能保证A。
假设系统,出现了不可预知的故障,但仍是能用,相比较正常的系统而言:

  • 响应时间上的损失:正常状况下的搜索引擎0.5秒即返回给用户结果,而基本可用的搜索引擎能够在2秒做用返回结果。
  • 功能上的损失:在一个电商网站上,正常状况下,用户能够顺利完成每一笔订单。可是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。

5.2 软删除(Soft State)

什么是软状态呢?相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种“硬状态”。
软状态指的是:容许系统中的数据存在中间状态,并认为该状态不影响系统的总体可用性,即容许系统在多个不一样节点的数据副本存在数据延时。

5.3 最终一致性(Eventually Consistent)

上面说软状态,而后不可能一直是软状态,必须有个时间期限。在期限事后,应当保证全部副本保持数据一致性,从而达到数据的最终一致性。这个时间期限取决于网络延时、系统负载、数据复制方案设计等等因素。
实际上,不仅是分布式系统使用最终一致性,关系型数据库在某个功能上,也是使用最终一致性的。好比备份,数据库的复制过程是须要时间的,这个复制过程当中,业务读取到的值就是旧的。固然,最终仍是达成了数据一致性。这也算是一个最终一致性的经典案例。

6 分布式一致性的五种解决方案

分布式涉及到数据一致性问题,数据库中经过事务确保数据一致性。所以分布式系统也可用经过分布式事务来确保分布式数据一致性。

6.1 2PC

2PC(Two-phase commit protocol),中文叫二阶段提交。 二阶段提交是一种强一致性设计,2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备和提交两个阶段。
注意这只是协议或者说是理论指导,只阐述了大方向,具体落地仍是有会有差别的。
让咱们来看下两个阶段的具体流程。

  • 准备阶段:协调者会给各参与者发送准备命令,你能够把准备命令理解成除了提交事务以外啥事都作完了。
  • 提交阶段:同步等待全部资源的响应以后就进入第二阶段即提交阶段(注意提交阶段不必定是提交事务,也多是回滚事务)。

假如在第一阶段全部参与者都返回准备成功,那么协调者则向全部参与者发送提交事务命令,而后等待全部事务都提交成功以后,返回事务执行成功。
让咱们来看一下流程图:
v2-880b5e0866906160b663827f961d8360_r.jpg
假如在第一阶段有一个参与者返回失败,那么协调者就会向全部参与者发送回滚事务的请求,即分布式事务执行失败
v2-83b3f422aeb89558c7c49f25811a2594_r.jpg
那可能就有人问了,那第二阶段提交失败的话呢?
这里有两种状况:

  • 第一种是第二阶段执行的是回滚事务操做,那么答案是不断重试,直到全部参与者都回滚了,否则那些在第一阶段准备成功的参与者会一直阻塞着。
  • 第二种是第二阶段执行的是提交事务操做,那么答案也是不断重试,由于有可能一些参与者的事务已经提交成功了,这个时候只有一条路,就是头铁往前冲,不断的重试,直到提交成功,到最后真的不行只能人工介入处理。

大致上二阶段提交的流程就是这样,咱们再来看看细节。

首先 2PC 是一个同步阻塞协议,像第一阶段协调者会等待全部参与者响应才会进行下一步操做,固然第一阶段的协调者有超时机制,假设由于网络缘由没有收到某参与者的响应或某参与者挂了,那么超时后就会判断事务失败,向全部参与者发送回滚命令。

在第二阶段协调者的无法超时,由于按照咱们上面分析只能不断重试!

2PC 是一种尽可能保证强一致性的分布式事务,所以它是同步阻塞的,而同步阻塞就致使长久的资源锁定问题,整体而言效率低,而且存在单点故障问题,在极端条件下存在数据不一致的风险。

6.2 3PC

3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,而且新增了一个阶段使得参与者能够利用这一个阶段统一各自的状态。

3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。

  • 准备阶段:什么都不作,就是问候一下,确认对方是否网络通畅。
  • 预提交阶段:对应2PC的准备阶段,除了提交什么都作了。
  • 提交阶段:提交或者回滚。

让咱们来看一下图:
v2-885daf4ba34102d6e1047b0b67910652_r.jpg
无论哪个阶段有参与者返回失败都会宣布事务失败,这和 2PC 是同样的(固然到最后的提交阶段和 2PC 同样只要是提交请求就只能不断重试)。

相对于2PC, 3PC多引入一个阶段也多一个交互,所以性能会差一些,并且绝大部分的状况下资源应该都是可用的,这样等于每次明知可用执行还得询问一次。

那么引入了超时机制,参与者就不会傻等了,若是是等待提交命令超时,那么参与者就会提交事务了,由于都到了这一阶段了大几率是提交的,若是是等待预提交命令超时,那该干啥就干啥了,反正原本啥也没干。

然而超时机制也会带来数据不一致的问题,好比在等待提交命令时候超时了,参与者默认执行的是提交事务操做,可是有可能执行的是回滚操做,这样一来数据就不一致了.

3PC 的引入是为了解决提交阶段 2PC 协调者和某参与者都挂了以后新选举的协调者不知道当前应该提交仍是回滚的问题。

新协调者来的时候发现有一个参与者处于预提交或者提交阶段,那么代表已经通过了全部参与者的确认了,因此此时执行的就是提交命令。

因此说 3PC 就是经过引入预提交阶段来使得参与者之间的状态获得统一,也就是留了一个阶段让你们同步一下。

可是这也只能让协调者知道该若是作,但不能保证这样作必定对,这其实和上面 2PC 分析一致,由于挂了的参与者到底有没有执行事务没法判定。

因此说 3PC 经过预提交阶段能够减小故障恢复时候的复杂性,可是不能保证数据一致,除非挂了的那个参与者恢复。

让咱们总结一下, 3PC 相对于 2PC 作了必定的改进:引入了参与者超时机制,而且增长了预提交阶段使得故障恢复以后协调者的决策复杂度下降,但总体的交互过程更长了,性能有所降低,而且仍是会存在数据不一致问题。

因此 2PC 和 3PC 都不能保证数据100%一致,所以通常都须要有定时扫描补偿机制

6.3 TCC

2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务,就像我前面说的分布式事务不只仅包括数据库的操做,还包括发送短信等,这时候 TCC 就派上用场了!
TCC 指的是Try - Confirm - Cancel。

  • Try 指的是预留,即资源的预留和锁定,注意是预留。
  • Confirm 指的是确认操做,这一步其实就是真正的执行了。
  • Cancel 指的是撤销操做,能够理解为把预留阶段的动做撤销了。

其实从思想上看和 2PC 差很少,都是先试探性的执行,若是均可以那就真正的执行,若是不行就回滚。

好比说一个事务要执行A、B、C三个操做,那么先对三个操做执行预留动做。若是都预留成功了那么就执行确认操做,若是有一个预留失败那就都执行撤销动做。

咱们来看下流程,TCC模型还有个事务管理者的角色,用来记录TCC全局事务状态并提交或者回滚事务。
v2-90179fa933c0a389ffa6ac04e244a58f_r.jpg
能够看到流程仍是很简单的,难点在于业务上的定义,对于每个操做你都须要定义三个动做分别对应Try - Confirm - Cancel。

所以 TCC 对业务的侵入较大和业务紧耦合,须要根据特定的场景和业务逻辑来设计相应的操做。

还有一点要注意,撤销和确认操做的执行可能须要重试,所以还须要保证操做的幂等。

相对于 2PC、3PC ,TCC 适用的范围更大,可是开发量也更大,毕竟都在业务上实现,并且有时候你会发现这三个方法还真很差写。不过也由于是在业务上实现的,因此TCC能够跨数据库、跨不一样的业务系统来实现事务。

6.4 本地消息表

本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。

本地消息表顾名思义就是会有一张存放本地消息的表,通常都是放在数据库中,而后在执行业务的时候 将业务的执行和将消息放入消息表中的操做放在同一个事务中,这样就能保证消息放入本地表中业务确定是执行成功的。

而后再去调用下一个操做,若是下一个操做调用成功了好说,消息表的消息状态能够直接改为已成功。

若是调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变动消息的状态。

这时候有可能消息对应的操做不成功,所以也须要重试,重试就得保证对应服务的方法是幂等的,并且通常重试会有最大次数,超过最大次数能够记录下报警让人工处理。

能够看到本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的状况。

6.5 消息队列

RocketMQ 就很好的支持了消息事务,让咱们来看一下如何经过消息实现事务。

第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来讲不可见,而后发送成功后发送方再执行本地事务。

再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。

而且 RocketMQ 的发送方会提供一个反查事务状态接口,若是一段时间内半消息没有收到任何操做请求,那么 Broker 会经过反查接口得知发送方事务是否执行成功,而后执行 Commit 或者 RollBack 命令。

若是是 Commit 那么订阅方就能收到这条消息,而后再作对应的操做,作完了以后再消费这条消息便可。

若是是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。

能够看到经过 RocketMQ 仍是比较容易实现的,RocketMQ 提供了事务消息的功能,咱们只须要定义好事务反查接口便可。
v2-72ba7bed684e855606c44ddda185987d_1440w.jpg
能够看到消息事务实现的也是最终一致性。消息事务与本地消息表差很少,只是它的小i西保存在消息队列。

7 一致性协议

8 一致性算法

9 参考

【1】分布式理论(一) - CAP定理
【2】分布式理论(二) - BASE理论
【3】分布式理论(三) - 2PC协议
【4】分布式理论(四) - 3PC协议
【5】分布式理论(五) - 一致性算法Paxos
【6】分布式理论(六) - 一致性协议Raft
【7】面试必问:分布式事务六种解决方案
【8】一分钟弄懂什么是分布式和微服务
【9】分布式优缺点

相关文章
相关标签/搜索