1.1. 一、分布式事务的3种架构。

一个应用程序使用分布式事务,有可能是下面的3种架构。

1.1.1. 1.1、单一服务,多个库。

单个服务,但是连接多个库。图形如下:

image-20200311142759932

这种架构,分布式事务的解决方案是:全局事务管理器。如:框架Atomikos。

1.1.2. 1.2、分库分表。

数据量大时,做分库分表,这时候也会产生分布式事务。

这时候分布式事务的解决方案是:中间件+数据库同步技术。

分库分表的中间件有:Mycat、Sharding-jdbc等。程序架构如下:

image-20200311144200206

1.1.3. 1.3、多服务、多数据源的分布式事务。

架构如下图:

image-20200311145932101

左边 A服务调用B、C服务。A、B、C 三个服务都各自操作了自己的数据库。这就是多服务,多数据源的操作。

服务A调用B、C服务时,同时操作了右边的4个数据库,要保持对这4个数据库的操作是一个事务。

解决方案是:需要全局事务的协调者全局事务管理器TM。协调和管理本地事务(也就是本地的资源管理器)。

1.2. 二、CAP理论 和 BASE 理论。

为了解决上面的多服务多数据源的分布式事务,我们需要知道CAP理论和BASE理论。可以把事务分作2类:

  • 刚性事务:实现了事务的ACID特性,本地事务特有的刚性事务。是强一致性的。
  • 柔性事务:CAP 和 BASE(分布式事务管理)。特点是"最终一致性"。

CAP 和 BASE 说的都是分布式系统中,一个数据多个备份的情况。参考百度百科

1.2.1. 2.1、CAP 理论。

CAP理论: 指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个要素最多只能同时实现两点,不可能三者兼顾。

  • C :是一致性(Consistency),在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
  • A:是可用性(Availability),在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)。就是可以在合理的时间返回合理的响应。也就是分布式系统是可用的。
  • P:是分区容错性(Partition tolerance),以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区(多个数据备份不一致)的情况,必须就当前操作在C和A之间做出选择。

但是在我们开发大数据量系统时,分区是避免不了的,分区容错(或者叫 集群可用)必须保证。所以只能实现AP、CP 系统。Zookeeper就是cp应用。

1.2.2. 2.2、BASE 理论。

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

BASE是对CAP中一致性(C) 和可用性(A) 权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),采用适当的方式来使系统达到最终一致性(Eventual consistency)。

  • 基本可用:以下两个就是“基本可用”的典型例子:

  • 响应时间上的损失:正常情况下,一个在线搜索引擎需要0.5秒内返回给用户相应的查询结果,但由于出现异常(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了1~2秒。

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

  • Soft state(软状态):也称为软状态,和硬状态相对,是指允许系统中的数据存在中间状态。比如:我们系统和微信支付系统的订单,可以设置订单为“支付中”,后面再同步系统的订单状态。这就是损失了强一致性(C),保证了可用性(A)。

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

1.3. 三、X/Open DTP模型与XA规范。

X/Open,即现在的open group,是一个独立的组织,主要负责制定各种行业技术标准。官网地址:http://www.opengroup.org。

就分布式事务处理(Distributed Transaction Processing,简称DTP)而言,X/Open主要提供了以下参考文档:

DTP 参考模型: Distributed Transaction Processing: Reference Model

DTP XA规范: Distributed Transaction Processing: The XA Specification

1.3.1. 3.1、DTP模型。

3.1.1、 模型元素

Distributed Transaction Processing: Reference Model 第3版中,规定了构成DTP模型的5个基本元素:

  • 应用程序(Application Program ,简称AP):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作。

  • 资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。

  • 事务管理器(Transaction Manager ,简称TM):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等。

  • 通信资源管理器(Communication Resource Manager,简称CRM):控制一个事务管理器作用域(TM domain)内或者跨事务管理器作用域的分布式应用之间的通信。

  • 通信协议(Communication Protocol,简称CP):提供CRM提供的分布式应用节点之间的底层通信服务。

    其中由于通信资源管理器(Communication Resource Manager)和通信协议(Communication Protocol)是一对好基友,从Communication Protocol的简称CP上就可以看出来,两个元素的关系不一般,因此有的文章在介绍DTP模型元素时,只提到了通信资源管理器。

3.1.2、 模型实例(Instance of the Model):

一个DTP模型实例,至少有3个组成部分:AP、RM、TM。如下所示:

image-20200312115516024

这张图类似于跨库事务,即单个应用需要操作多个库。这里是一个AP需要操作多个RM上的资源。AP通过TM来声明一个全局事务,然后操作不同的RM上的资源,最后通知TM来提交或者回滚全局事务。

上面的图也就是单个应用需要操作多个库 的架构。

下面的图显示了,一个事务管理器作用域(TM domian)内,多个应用。

image-20200312121840632

上游节点(superior node)调用下游节点(subordinate node ),但是他们处于一个事务管理器作用域(TM domian)内。也就是上级服务调用下级服务,但是整个服务整体处于一个事务管理器管理。

上游节点(superior node)调用下游节点(subordinate node )通信使用通信资源管理器(CRM):与传统RPC框架不同,CRM底层采用OSI TP(Open Systems Interconnection — Distributed Transaction Processing)通信服务,因此CRM具备事务传播能力。这一点体现TM、CRM之间的连线。

3.1.2、 XA规范:

XA规范的最主要的作用,就是定义了RM-TM的交互接口,如下图:XA仅仅出现在RM和TM的连线上。

image-20200312143419383

XA规范的作用就是定义了事物管理器TM和资源管理器RM的交互接口。

在DTP参考模型(Distributed Transaction Processing: Reference Model)中,指定了全局事务的提交要使用二阶段提交(two-phase commit,简写为2PC)协议;而XA规范(<< Distributed Transaction Processing: The XA Specification>>)只是定义了两阶段提交协议中需要使用到的接口。

1.4. 四、分布式事务的实现方式。

这里是基于BASE理论的柔性事务的实现方式。有几下几种:

1.4.1. 4.1、二阶段提交(two-phase commit,简写为2PC)。

二阶段提交的实现是TCC和XA。是一种事务补偿方案。下图是TCC和XA的比较:

image-20200312144903244

TCC事务的处理流程与2PC两阶段提交类似,不过2PC通常都是在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。这种分布式事务的实现方式的优势在于,可以让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。而TCC不足之处则在于对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口还必须实现幂等。

下面是二阶段提交的过程

  • 阶段1:

    TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。在发送了否定答复并回滚了已经的工作后,RM就可以丢弃这个事务分支信息。

    以mysql数据库为例,在第一阶段,事务管理器向所有涉及到的数据库服务器发出prepare"准备提交"请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成"可以提交",然后把结果返回给事务管理器。

  • 阶段2

    TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。

​ 以mysql数据库为例,如果第一阶段中所有数据库都prepare成功,那么事务管理器向数据库服务器发出"确认提交"请求,数据库服务器把事务的"可以提交"状态改为"提交完成"状态,然后返回应答。如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会把"可以提交"的事务回撤。

XA规范对两阶段提交协议有2点优化:

只读断言

在Phase 1中,RM可以断言“我这边不涉及数据增删改”来答复TM的prepare请求,从而让这个RM脱离当前的全局事务,从而免去了Phase 2。

这种优化发生在其他RM都完成prepare之前的话,使用了只读断言的RM早于AP其他动作(比如说这个RM返回那些只读数据给AP)前,就释放了相关数据的上下文(比如读锁之类的),这时候其他全局事务或者本地事务就有机会去改变这些数据,结果就是无法保障整个系统的可序列化特性——通俗点说那就会有脏读的风险。

一阶段提交

如果需要增删改的数据都在同一个RM上,TM可以使用一阶段提交——跳过两阶段提交中的Phase 1,直接执行Phase 2。

这种优化的本质是跳过Phase 1,RM自行决定了事务分支的结果,并且在答复TM前就清除掉事务分支信息。对于这种优化的情况,TM实际上也没有必要去可靠的记录全局事务的信息,在一些异常的场景下,此时TM可能不知道事务分支的执行结果。

两阶段提交协议(2PC)存在的问题

1、同步阻塞问题。两阶段提交方案下全局事务的ACID特性,是依赖于RM的。例如mysql5.7官方文档关于对XA分布式事务的支持有这个文档:https://dev.mysql.com/doc/refman/5.7/en/xa.html

里面说明了,一个全局事务内部包含了多个独立的事务分支,这一组事务分支要不都成功,要不都失败。各个事务分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支的支持的ACID特性提升一个层次(up a level)到分布式事务的范畴。

括号中的内容的意思是: 即使在非分布事务中(即本地事务),如果对操作读很敏感,我们也需要将事务隔离级别设置为SERIALIZABLE。而对于分布式事务来说,更是如此,可重复读隔离级别不足以保证分布式事务一致性。

也就是说,如果我们使用mysql来支持XA分布式事务的话,那么最好将事务隔离级别设置为SERIALIZABLE。SERIALIZABLE(串行化)是四个事务隔离级别中最高的一个级别,也是执行效率最低的一个级别。

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

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

由于二阶段提交存在着诸如同步阻塞、单点问题等缺陷,所以,研究者们在二阶段提交的基础上做了改进,提出了三阶段提交。

1.4.2. 4.2、三阶段提交(3PC)。

三阶段提交(3PC)对二阶段提交(2PC)有2个改进点:

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

  2. 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。

image-20200312163333898

  • CanCommit阶段

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

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

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

  • PreCommit阶段

    协调者(Coordinator)根据参与者(Partcipant)的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。

    假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。

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

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

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

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

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

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

  • doCommit阶段

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

    Case 1:执行提交

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

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

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

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

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

  • 发送中断请求: 协调者向所有参与者发送abort请求

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

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

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

    在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 )

2PC与3PC的区别:

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

了解了2PC和3PC之后,我们可以发现,无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。Google Chubby的作者Mike Burrows说过:世上只有一种一致性算法,那就是Paxos,所有其他一致性算法都是Paxos算法的不完整版。

1.5. 五、典型的柔性事务方案

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

典型的柔性事务解决方案如下:

  • TCC(两阶段型、补偿型)。
  • 可靠消息最终一致性(异步确保型)。比如:用消息中间件mq保证事务。但是要注意,可靠消息需要mq保证是支持事务的。这样才能保证消息确实已经发送出去。如:RocketMQ就支持事务型消息。
  • 最大努力通知(非可靠消息、定期校对)。比如:滴滴打车中,发信息提示用户去支付。

1.6. 转载自文档

http://www.tianshouzhi.com/api/tutorials/distributed_transaction

results matching ""

    No results matching ""