分布式事务处理哲学

Posted 架构的哲学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式事务处理哲学相关的知识,希望对你有一定的参考价值。

我们经常谈分布式,分布式,分布式系统来解决系统耦合性的同时,也对系统的一致性带来很大的挑战。


在单体应用时代,数据的一致性可以通过ACID来保证,此时的系统可以做到CA。


我们先来复习一下什么是CAP理论和ACID。

分布式事务处理哲学

CAP理论是指在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。分布式场景下,往往P是一定需要保证的,所以分布式系统通常的模式是CP或者AP。

CA:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的。传统的关系型数据库RDBMS:Oracle、mysql就是CA。

CP:如果不要求A(可用),相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。设计成CP的系统其实不少,最典型的就是分布式数据库,如Redis、HBase等。对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库就好,没必要再浪费资源来部署分布式数据库。

AP:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。典型的应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不至于造成用户购物流程的严重阻塞。


其中有一类特殊的CP系统,ACID,常见于关系型数据库。

ACIDAtomicity原子性,Consistency一致性,Isolation隔离性,Durability持久性)是事务的特点,具有强一致性,一般用于单机事务,分布式事务若采用这个原则会丧失一定的可用性

分布式事务处理哲学


通常,绝大部分场景都不会要求严格的强一致性,主流的一致性协议一般都选择的是弱一致性的特殊版本:最终一致性


其中BASE理论就是最终一致性的基石。

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


说了这么多理论,那到底分布式事务该如何来保证呢?


一、TCC模式

TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

分布式事务处理哲学

  • 实现:一个完整的业务活动由一个主业务服务于若干的从业务服务组成。主业务服务负责发起并完成整个业务活动。从业务服务提供TCC型业务操作。业务活动管理器控制业务活动的一致性,它登记业务活动的操作,并在业务活动提交时确认所有的TCC型操作的Confirm操作,在业务活动取消时调用所有TCC型操作的Cancel操作。

  • 成本:实现TCC操作的成本较高,业务活动结束的时候Confirm和Cancel操作的执行成本。业务活动的日志成本。

  • 使用范围:强隔离性,严格一致性要求的业务活动。适用于执行时间较短的业务,比如处理账户或者收费等等。

  • 特点:不与具体的服务框架耦合,位于业务服务层,而不是资源层,可以灵活的选择业务资源的锁定粒度。TCC里对每个服务资源操作的是本地事务,数据被锁住的时间短,可扩展性好,可以说是为独立部署的SOA服务而设计的。

二、基于可靠消息的最终一致性方案

分布式事务处理哲学

  • 实现:业务处理服务在业务事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不是真正的发送。业务处理服务在业务事务提交之后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才会真正发送。

  • 消息:业务处理服务在业务事务回滚后,向实时消息服务取消发送。消息发送状态确认系统定期找到未确认发送或者回滚发送的消息,向业务处理服务询问消息状态,业务处理服务根据消息ID或者消息内容确认该消息是否有效。被动方的处理结果不会影响主动方的处理结果,被动方的消息处理操作是幂等操作。

  • 成本:可靠的消息系统建设成本,一次消息发送需要两次请求,业务处理服务需要实现消息状态回查接口。

  • 优点:消息数据独立存储,独立伸缩,降低业务系统和消息系统之间的耦合。对最终一致性时间敏感度较高,降低业务被动方的实现成本。兼容所有实现JMS标准的MQ中间件,确保业务数据可靠的前提下,实现业务的最终一致性,理想状态下是准实时的一致性。

三、两阶段提交(2PC)

两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。

1. 执行过程

  1. 提交事务请求(投票阶段)

    • 协调者参与者发送事务内容,询问是否可以执行事务提交操作,等待响应

    • 参与者执行事务操作,并将undoredo日志记录

    • 参与者回复协调者,执行成功则回Yes否则No

  2. 执行事务提交(执行阶段)

    • 如果都是参与者都回复Yes,则协调者向参与者发送提交请求,否则发送回滚请求

    • 参与者根据协调者的请求执行事务提交或回滚,并向协调者发送Ack消息

    • 协调者收到所有的Ack消息过后判断事务的完成或者中断

2.存在的问题

2.1 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。

2.2 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。

2.3 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。

2.4 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制


三、三阶段提交(3PC)

1. 执行过程

  1. 事务询问(canCommit

    • 协调者向参与者发送一个包含事务内容的询问请求,询问是否可以执行事务并等待

    • 参与者根据自己状态判断并回复yes、no

  2. 执行事务预提交(preCommit

    • 若协调者收到全是yes,就发送preCommit请求否则发布abort请求

    • 参与者若收到preCommit则执行事务操作并记录undo和redo然后发送Ack,若收到abort或者超时则中断事务

  3. 执行事务提交(doCommit

    • 协调者收到所有的Ack则发送doCommit请求,若收到了No或者超时则发送abort请求

    • 参与者收到doCommit就执行提交并发送ACk,否则执行回滚并发送Ack

    • 协调者收到Ack判断是完成事务还是中断事务

2.存在的问题

三阶段相对于两阶段的改善就是把准备阶段一分为二,亦即多了一个canCommit阶段,按我理解这样就类似于TCP的三步握手,多了一次确认,增大了事务执行成功的概率。而且3PC的协调者即使出了故障,参与者也能继续执行事务所以解决了2PC的阻塞问题,但是也可能因此导致集群数据不一致。


以上是关于分布式事务处理哲学的主要内容,如果未能解决你的问题,请参考以下文章

Spring针对事务处理提供哪两种事务编程模式。

BottomNavigationView 滞后于片段事务

关于DataSet事务处理以及SqlDataAdapter四种用法

基于 HBase 构建可伸缩的分布式事务队列

分布式事务处理方案,微服事务处理方案

分布式事务处理方式