• 分布式事务(一)原理概览


    系列目录

    分布式事务(一)原理概览

    分布式事务(二)JTA规范

    分布式事务(三)mysql对XA协议的支持

    分布式事务(四)简单样例

    分布式事务(五)源码详解

    分布式事务(六)总结提高

    一、引子

    事务(数据库事务)是java开发工程师必须掌握的一项技能。又可分为本地事务和分布式事务,其中分布式事务是进阶为高级开发工程师必会的技能。本文从概念、原理、实践多角度剖析分布式事务,希望有所收获。

    二、概念

    2.1.本地事务

    大部分情况下,一个服务操作一个数据库,这就是本地事务,ACID特性由数据库提供支持,比如mysql innodb引擎。如下图所示(网上的图,挺好直接用):

    spring 提供了2种方式实现:

    • 编程式:基于transactionTemplate去实现,适合手动精准控制事务的场景,少用。
    • 声明式事务注解:@Transactional加在serviceImpl的方法上即可,这也是常用的方法。

    关于本地事务这里不多说,飞机票:本地事务飞机票。

    2.2.分布式事务

    当遇到复杂业务调用时,可能会出现跨库多资源调用(一个事务管理器,多个资源)/多服务调用(多个事务管理器,多个资源),期望全部成功或失败回滚,这就是分布式事务,用以保证“操作多个隔离资源的数据一致性”。

    2.3 相关协议发展历史

    三、DTP模型 & XA规范

    背景

    Mysql官方对于XA事务,描述如下:

    1. Mysql InnoDB引擎支持分布式事务,mysql的XA实现是基于X/Open CAE 文档中的 Distributed Transaction Processing: The XA Specification. (DTP XA规范)的。飞机票:13.3.7 XA Transactions官方飞机票。X/Open CAE解释如下:
    • X/Open: X/Open是一个独立的、全球性的开放系统组织,由世界上最大的信息系统供应商、用户组织和软件公司支持。其使命是通过开放系统的实际实施,为用户带来更大的计算价值。
    • X/Open CAE规范: 即X/Open Common Applications Environment,这个环境覆盖了高于硬件级别的,支持开放系统所需的一组标准。它提供了应用程序的可移植性和互操作性。
    1. MySQL Connector 5.0.0及更高版本直接支持XA,通过一个类接口为您处理XA SQL语句接口。XA支持分布式事务,即允许多个单独的事务资源参与全局事务。事务资源通常是rdbms,但也可能是其他类型的资源。
    2. X/Open CAE文档是发布在open group官网上的,http://www.opengroup.org/public/pubs/catalog/c193.htm

    在open group官网可查到,有2个XA规范,一个是XA,一个是XA+, 其中XA+是XA的超集,新定义了通信资源管理器CRM的协议,建议直接看XA+即可。后续分析直接以XA+ 1994版为准。官方下载链接如下:

    看名字我们就知道 XA规范是依托于DTP场景的,下面我们分别从DTP模型、XA规范2个视角来剖析原理

    3.1 DTP模型

    依据X/Open《Distributed Transaction Processing: Reference Model, Version 3》上的介绍,DTP模型是一种软件体系结构,它允许多个应用程序共享多个资源管理器提供的资源,并允许将它们的工作协调到全局事务中。

    3.1.1 模型元素

    要深度了解DTP,先看看模型内的元素概念,如下:

    • 应用程序(Application Program ,简称AP):每个AP指定一个包含资源(如数据库)的操作序列。AP定义全局事务的开始和结束,访问事务边界内的资源,通常决定是提交还是回滚每个事务。
    • 资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。
    • 事务管理器(Transaction Manager ,简称TM):管理全局事务,负责分配事务唯一标识XID,监控事务的执行进度,并负责事务的提交、回滚等。如果RM是一个通信资源管理器(CRM),那么在执行两个APs之间的通信时,它将xid传递给合作伙伴、从属的CRMs。
    • 通信资源管理器(Communication Resource Manager,简称CRM):控制一个TM域(TM domain)内或者跨TM域的分布式应用之间的通信。
    • 通信协议(Communication Protocol,简称CP):一种通信协议,它提供分布式应用程序使用的、由CRMs支持的底层通信服务。

    介绍完模型元素,下面来看2种典型的DTP场景,一种是单应用跨库DTP,另一种是跨应用DTP。

    3.1.2 单应用跨库DTP

     一个应用使用一个事务管理器TM,操作多个资源管理器RMs,如下图:

    3.1.3 跨应用DTP

    如果分布式事务需要跨多个应用,例如微服务调用,那就必须增加通讯资源管理器CRMs(跨应用管理事务),如下图:

    上图中使用的接口如下:

    1. AP-RM接口 : 允许AP访问资源,如SQL和ISAM,提供AP可移植性。
    2. AP-TM接口 : 即TX接口,为AP提供了一个API, AP通过API与TM协调全局事务管理。
    3. TM-RM接口 : 即XA接口,允许TM将RMs的工作构造为全局事务,并协调完成或恢复。XA接口是TM与RM之间的双向接口。
    4. TM-CRM接口 : 即XA+接口,支持跨TM域的全局事务信息流。XA+ 接口是TM与CRM之间的双向接口。
    5. AP-CRM接口 : 为全局事务中的多应用之间的DTP通信提供了可移植的api,例如:TxRPC、XATMI、Peer-to-Peer。
    6. CRM-OSI TP接口 : 即XAP-TP接口,提供了CRM和OSITP(Open Systems Interconnection — Distributed Transaction Processing)服务之间的编程接口。X/Open定义了这个接口来支持特定于应用程序的OSI服务的可移植实现。

    本节我们剖析了DTP模型,以及XA接口在DTP中的作用,下面我们来更详细的看一下XA规范。

    3.2 XA规范

    通过上面的分析,我们知道XA和XA+规范的使用场景,如下图所示:

    下面来具体看一下XA/XA+接口定义的函数群。其中带+号的是XA+规范,不带+号的是XA规范。

    3.2.1 xa_*()函数群

    TM通过xa_*()函数调用RM。当AP调用TM启动全局事务时,TM可以使用xa_interface通知事务分支的RMs。AP使用RM的本机接口完成支持全局事务的工作后,TM调用xa_()函数提交或回滚分支。xa_()函数如下表所示:

    3.2.2 ax_*()函数群

    RM通过ax_*()函数调用TM。所有的TMs都必须提供这些功能。这些函数允许RM动态地控制它在事务分支中的参与。此外,CRMs使用ax_interface创建事务分支,挂起或完成事务分支,并将承诺协议传播到事务分支。ax_()函数如下表所示:

    关于XA/XA+的具体方法如何调用流程这里就不再提供。有兴趣的自己看规范原文。

    3.3 两阶段提交-2PC

    XA协议中有一个细节:按照OSITP标准(模型)的定义,TMs和RMs使用两阶段提交全局事务。

    3.3.1 XA的两阶段提交模型

    如上图,XA规范实现的两阶段提交流程:(下面全部翻译自XA规范原文)

    阶段1

      TM要求所有RMs准备提交(或准备)事务分支。这询问RM是否能够保证提交事务分支的能力。RM可能会查询该RM内部的其他实例。CRM被要求准备它们创建的事务分支,将prepare请求发送到远程站点并接收结果。在返回失败并回滚其工作之后,RM可以丢弃事务分支的信息。

    阶段2

      TM根据实际情况向所有RMs发出提交或回滚事务分支的请求。CRM被要求提交或回滚它们创建的事务分支,向远程站点发送提交或回滚请求并接收结果。所有RMs提交或回滚对共享资源的更改,然后将状态返回给TM。然后TM可以丢弃全局事务的信息。

    3.3.2 XA对2PC的优化

    1.只读断言

      当事务分支没有更新共享资源时,这个RM会断言并响应给TM的prepare请求。也就免去了阶段2。但是,如果一个RM在全局事务的所有RMs返回prepared之前返回了只读优化,该RM释放事务上下文,例如read locks。这时候其他事务就有机会去改变这些数据(可能是写锁),显然全局序列化被破坏。同样CRM也可以断言,当TM挂起或终止线程与事务分支的关联时,它不是某个特定线程中活动的事务分支的参与者。

    2.一阶段提交

      如果一个TM知道DTP系统中只有一个RM在修改共享资源,那么它可以使用单阶段提交。即TM免去了阶段1的prepare,直接执行了阶段2的commit。

    3.3.3 2PC的缺点

    1.资源阻塞

    由于协调者的重要性,一旦协调者TM发生故障。参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)

    2.数据不一致

    在阶段二,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。

    由于二阶段提交存在着这些缺陷,所以,研究者们在二阶段提交的基础上做了改进,提出了三阶段提交。

    3.4 三阶段提交-3PC

    3PC,即Three-phase commit protocol,由一个协调者领导事务(领头人),和一组被指导的参与者(同伙)组成。协调者和参与者都有超时执行机制,如下图:

    阶段1:

      【协调者】接收事务请求。如果此时出现故障,协调者将中止事务,否则,协调者发送一个canCommit给所有参与者,并切换到waiting状态。

      【参与者】获得了canCommit的请求,如果同意,它将向协调者发送Yes消息并切换到prepared状态。否则它将发送No消息并中止。如果出现故障,它将移动到abort状态。

    阶段2:

      【协调者】在一个时间段内,接收来自所有参与者的Yes消息,向所有参与者发送preCommit消息并切换到prepared状态。如果出现故障、超时或协调者(prepared状态)接收到No消息,将中止事务并向所有参与者发送abort消息。

      【参与者】收到preCommit消息,它将发送ACK消息并等待最后的提交或中止。如果接收到中止消息、失败或等待提交的超时,它将中止。

    阶段3:

      【协调者】在收到来自大多数参与者的确认的情况下,协调者切换到commit状态。并向所有参与者发送doCommit请求。如果协调者在等待一个参与者的ack时超时了,它将中止事务。

      【参与者】参与者接收到doCommit请求之后,执行正式的事务提交,并发送ack给协调者。注:超时未收到消息,一样会提交事务!!!!

    3.5 总结

    上面讲解了2pc、xa、3pc,比较如下:

    协议/优缺点 优点 缺点
    2PC 逻辑简单,容易理解。

    1.全程阻塞。例如:TM故障,RM阻塞资源。

    2.网络故障时,部分commit,数据一致性无法保证。

    XA(提交时使用2PC规范)

    1.只读断言

    2.可进化为一阶段提交

    全局序列化被破坏。脏读问题。
    3PC

    引入双边超时机制,避免阻塞。

    1.需要3次请求返回,可能会有长延迟,性能低。

    2.基于失败-停止(fail-stop)模型,出现网络问题,无法恢复。

     

     

     

     

    问题:

    上面3种协议都无法解决分布式系统下的数据一致性问题,只有Paxos算法,才能彻底解决该问题。paxos飞机票:底层算法系列:Paxos算法。具体实践中,为了提高可用性(性能)一般很少做到强一致性。且大批的技术先驱们已经总结出了一套理论,让我们有理可依。

    四、CAP理论

    2000年7月,Eric Brewer教授在ACM PODC会议上提出CAP猜想。Brewer认为在设计一个大规模的分布式系统时会遇到三个特性:一致性(consistency)、可用性(Availability)、分区容错(partition-tolerance),而一个分布式系统最多只能满足其中的2项。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。

    1. 一致性(Consistency)

    一致性指“all nodes see the same data at the same time”,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,不能存在中间状态。

    强一致性所有节点在同一时间的数据完全一致,那么称之为强一致性。

    弱一致性:此外,如果允许存在部分数据不一致,那么就称之为弱一致性。

    最终一致性:如果允许存在中间状态,只要求经过一段时间后,数据最终是一致的,则称之为最终一致性。

    2. 可用性(Availability)

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

    3. 分区容错性(Partition tolerance)

        分区容错的意思是,节点间通信可能失败,仍然需要能够保证对外提供满足一致性和可用性的服务。

    4.1 分析

    首先我们必须保证P(分区容错性),才能称之为一个分布式系统,因此只能在C(一致性)和A(可用性)之间寻求平衡。而前面我们提到的X/Open XA 两阶段提交协议的分布式事务方案,强调的就是一致性。并且由于其阻塞执行效率低,且当网络出现问题时也无法真正保证数据一致性,实际应用的并不多。而基于BASE理论的柔性事务,强调的是可用性,目前大行其道,大部分互联网公司采可能会优先采用这种方案。(有的同学问为啥不用paxos?实现过于复杂,且保证了强一致性,想一想也知道性能会有损耗,所以一般也不用!)

    五、BASE理论

    2008年7月28日,eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论。文章链接:https://queue.acm.org/detail.cfm?id=1394128

        BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consistency)。    

    BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。

        1. 基本可用(Basically Available)

            指分布式系统在出现不可预知故障的时候,允许损失部分可用性。

        2. 软状态( Soft State)

            指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性。

        3. 最终一致( Eventual Consistency)

            强调的是所有的数据更新操作,在经过一段时间的同步之后,最终都能够达到一个一致的状态。

        BASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的。通过牺牲强一致性来获得可用性,允许数据在一段时间内是不一致的,但最终达到一致状态。实际应用中,会在对数据库操作进行本地事务(ACID特性)+Eventually consistent最终一致性(BASE理论)结合使用

    那么如何实现分布式环境下数据的最终一致性呢?

    六、最终一致性方案(柔性事务)

    实践中,有些高可用场景下,不必要强一致性,只需要最终一致性即可,这在业内称呼为"柔性事务",也就是最终一致性方案,是遵循BASE理论设计出来的。

    6.1 正向幂等重试+反向异步回调(最大努力通知型)

    对于某些非核心service,可以采取正向重试机制。比如一个请求超时失败了,可以再重试请求几次,一直到接收到成功返回或者达到重试次数为止。注意这里要保证接口的幂等性。即多次调用结果一样。

    很多调用第三方的接口(比如征信接口,耗时比较长),接口是异步回调型。请求方发送请求后,等待第三方异步回调自己的返回结果接口。

    这两种机制都是不可靠的,必要时刻可以两者相结合使用。如下图所示:

    6.2 可靠消息最终一致异步确保型)

    这里不讲解已支持分布式事务的MQ.

    可靠消息就是使用独立的消息服务,使用“预发送机制”把消息提前入库,业务确定执行完毕,再修改消息状态为可发送,然后再发送消息给MQ,消费者再消费。

    预发送机制

      如果先执行业务,再发消息(先发消息再执行业务也不行),入kafka,可能立刻就消费了。本地事务回滚是无法回滚已发送到kafka的消息的。使用预发送机制,保证了消息服务DB中有一条“初始化”状态的消息记录。业务异常,就不会“确认发送Msg”,消息就不会发送。

    可靠场景下,甚至可以在消息服务中轮询”初始化“状态的且过了“一个时间段”(一般超过这个时间,肯定是出问题了)的消息,再去查询业务系统是否完成,如果完成则自修复成"待发送"状态。

    注:上图主业务作为生产者,严格来说,消息服务平台才是生产者(如果把kafka作为中心的话)。

    整个流程如上图:

    1. 消息入库:主业务系统,初始化一条消息进msg表,state=初始化。
    2. 确认可发送:更新消息状态state=待发送。
    3. send生产消息:定时轮询state=待发送 的消息,send给kafka。
    4. pull消费消息:从业务作为消费者主动去kafka 拉取消息消费。
    5. ack告知kafka:消费成功告知kafka。
    6. 告知消息服务:消费成功告知消息服务,更新状态state=完成,或者删除消息记录。(如果场景要求较高建议留下存根)

     本地消息表(业务系统+消息表,强一致性)

      如果不使用独立的消息服务平台,在业务系统内部新建一张消息表,就可以完全由一个本地事务来控制,这样第1、2步的“消息入库”、“确认发送”可以确保成功,也就不需要“轮询自修复”了,如果公司不要求使用统一消息服务平台的话,使用本地消息表也是ok的。

    6.3 TCC(两阶段补偿型)

    TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC 实质上是应用层的2PC(2 PhaseCommit, 两阶段提交),好比把 XA 两阶段提交那种在数据资源层做的事务管理工作提到了数据应用层。TCC流程如下图:

    如上图所示,步骤:

    • 阶段1

        主业务活动请求(try)各个从业务服务预留资源。try过程的本地事务,是保证资源预留的业务逻辑的正确性。

    • 阶段2

        如果在第一阶段所有业务资源都预留成功,那么confirm各个从业务服务,否则取消(cancel)所有从业务服务的资源预留请求。

    优点:

    相比XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。

    TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。confirm/cancel执行的本地事务逻辑确认/取消预留资源,confirm和cancel就是补偿型事务(Compensation-Based Transactions)。注意:confirm和cancel都是独立的本地事务,是对try的补偿。

    缺点:

    针对一个请求,需要从业务服务提供3个接口,供主业务服务调用,业务方改造成本高。

    ====参考=======

    分布式事务 :第一节很多都是参考本文,写的不错。

  • 相关阅读:
    #Leetcode# 541. Reverse String II
    PAT 甲级 1030 Travel Plan
    PAT 甲级 1029 Median
    bzoj 2002 [Hnoi2010]Bounce 弹飞绵羊
    jzoj 4243. 【五校联考6day1】c
    2019.02.23【NOIP提高组】模拟 A 组 总结
    【GDOI2013模拟1】病毒传播
    【GDOI2013模拟1】最短路
    【GDOI2013模拟1】删数字
    数列分块入门 6 总结
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/10580446.html
Copyright © 2020-2023  润新知