分布系统中,如何保证数据的一致性、原子性,分布式事务。分布式事务分为两大类,柔性事务、刚性事务。
一、方法论篇
分布式事务主要分为两部分,刚性事务和柔性事务。刚性事务主要针对DB层面,严格保证事务的原子性要么都成功,要么执行失败,全部回滚。
柔性事务,相对于刚性事务来的,为了保证DB的利用率,以及系统的吞吐量,不会长时间锁定DB资源,在事务执行失败之后不会进行回滚,而是采用补偿的方式保证数据的最终一致性,所以柔性事务又叫补偿型事务。先来介绍刚性事务。
1.1刚性事务
X/Open XA 协议,最早的分布式事务模型是 X/Open 国际联盟提出的 X/Open Distributed Transaction Processing(DTP)模型,也就是大家常说的 X/Open XA 协议,简称XA 协议。
名词解释:
- 其中应用程序(Application Program ,简称AP):AP定义事务边界(定义事务开始和结束)并访问事务边界内的资源。
- 资源管理器(Resource Manager,简称RM):Rm管理计算机共享的资源,许多软件都可以去访问这些资源,资源包含比如数据库、文件系统、打印机服务器等。
- 事务管理器(Transaction Manager ,简称TM):负责管理全局事务,分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚、失败恢复等。
操作过程
- 第一阶段,AP发起事务,TM要求所有的RM准备提交对应的事务分支,询问RM是否有能力保证成功的提交事务分支,RM根据自己的情况,如果判断自己进行的工作可以被提交,那就就对工作内容进行持久化,并给TM回执OK;否者给TM的回执NO。RM在发送了否定答复并回滚了已经的工作后,就可以丢弃这个事务分支信息了。
- 第二阶段TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare回执NO的话,则TM通知所有RM回滚自己的事务分支。
其本质就是一个2PC过程
1.1.1 2PC
第一阶段,资源预占阶段(准备阶段),所有RM回复OK,则提交事务,否则失败。
第二阶段,事务提交阶段(资源执行阶段),所有执行成功则执行成功,否则返回失败。
1.1.2 3PC
1.1.3 小结
刚性事务是一个阻塞性的事务(DB层面的一种定义)会占用,过多资源,并发度低,会导致吞吐量偏低。一般不建议在耗时较大的分布式事务中使用刚性事务。
1.2柔性事务
就过程本质上也是2pc阶段,只不过在应用层面实现减少对DB的持有时间,是一种补偿型事务,事务执行失败,进行补偿,最终保证事务成功,而不做回滚。其实利用的Base理论,保证服务的基本可用,是CAP模型中AP模型的演化结果。
ba(base available)基本可用;
s(soft state)中间状态;
e(eventual consistency)最终一致性的理论。
1.2.1TCC
TCC(Try-Confirm-Cancel),期模型完全交由业务方实现,每个在业务中的子模块都需要实现Try,Confirm,Cancel三个接口,对业务入侵极大。有原来的一个接口就实现完的内容现在需要二外的多增加两个接口,开发量增加了两倍。TCC让应用程序自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。
名词解释:
T(Try):完成业务方面的检查,预留相关的资源。假设一个业务完成需要A,B,C三个子事务完成,这时候需要调用A,B,C三个业务的Try接口。
C(Confirm):成功预占资源(A,B,C三个Try都成功),真正的执行业务;
C(Cancel):事务执行失败(A,B,C三个Try只要有一个失败),释放Try阶段的资源。
问题:超时如何处理?
超时需要重试,所以接口实现接口需要幂登。
1.2.2Saga
- 每个Saga将一个大的分布式事务拆分为若干sub-transaction Ti
- 每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果
- 当Saga事务中的任何一个本地事务执行失败之后,调用补偿方法恢复之前的事务,达到最终一致
- 恢复的方式
1.2.3TCC与Saga比较
TCC
缺点:
1、业务侵入性高,每一个本地事务都需要开发三个接口;
2、较原来与本地事务的交互次数多了一倍,较原来多了一个Confirm或者Cancel;
3、一旦进入Confirm阶段必须保证成功,不成功需要人工接入;
优点:
资源通过Try提前占用,数据能得到较好的隔离;
Sage
优点:
1、性能开销较小,在执行成功的情况下只需要一次交互;
2、对现有业务侵入性小,现有业务几乎不需要改变,只需要增加补偿接口;
缺点
1、注意数据隔离性的问题,事务执行失败,需要关注该次事务执行的没有回滚的数据被别的事务利用,会产生脏读的问题;业务上需要控制每个事务的隔离级别。
二、实战架构篇
这里主要给大家介绍柔性分布式事务的实战。鉴于刚性事务一般会被底层存执直接实现以及由于占有资源相时间较长吞吐量交差,这里就不做介绍了。柔性事务分为两种,同步事务和异步事务。
2.1 异步事务
通过消息组件驱动的事务成为异步事务。异步事务的关注点在于必须保证发送MQ消息和本地事务执行是一个原子操作。这时候需要用户事务消息。
2.1.1 基于事务消息的异步事务
消息会查:由于网络闪断,服务重启等原因,导致半消息没有进行二次确认,或者二次企确认消息丢失,这是MQ发现某条消息长时间处于“暂不投递”状态,需要主动向消息生产者查询该消息的最终状态(是commit,还是rollback),这个需要生产者自己来实现call back。
2、不支持幂等,可能会产生多天这样的消息。需要消费端做幂登处理;
3、发送消息失败可能会导致业务执行失败。
2.1.2 本地消息表架构
本质上就是将原来的事务消息改成本地消息表,在一个本地事务中完成消息落地和本地业务事务,然后通过微服务扫描未发送的消息,投递到MQ Server中去。
缺点:需要增加一张与业务无法的表。
优点:
1、将消息服务和业务解耦解除MQ对业务的影响;
2、不用开发消息回查,减少主业务的侵入。
2.2 同步事务
一个分布式事务,所依赖的本地事务都需要直接调用完成,就称之为同步事务。
2.2.1 基于TCC事务架构
TCC 分布式事务模型包括三部分:
- 主业务服务:主业务服务为整个业务活动的发起方,服务的编排者,负责发起并完成整个业务活动。
- 从业务服务:从业务服务是整个业务活动的参与方,负责提供 TCC 业务操作,实现初步操作(Try)、确认操作(Confirm)、取消操作(Cancel)三个接口,供主业务服务调用。
- 业务活动管理器:以jar包组件的形式抽离出来,控制整个业务活动,包括记录维护 TCC 全局事务的事务状态和每个从业务服务的子事务状态,并在业务活动提交时调用所有从业务服务的 Confirm 操作,在业务活动取消时调用所有从业务服务的 Cancel 操作。
2.2.2 Sage架构实战
利用Fast Fail的思想在事务执行失败的情况下直接快速返回,然后异步执行补偿,下面是整体方案执行架构。将补偿服务的调用和事务的控制过程通过AOP+微服务+注册中心的形式抽离出来,让后业务仅仅关注业务即可。
包含三个部分
- 微服务,是业务服务,主要处理实际业务,也是分布式事务的发起者,使用分布式事务只需要加入注解即可;
- 分布式事务Proxy组件,对事务进行追踪管理以及收集每个本地事务的入参,回滚方法,以及事务执行结果,然后将这些数据落地,共补偿事务服务调用使用;
- 分布式事务补偿服务,扫描TDB(事务数据落地的DB),执行事务失败且没有补偿成功的事务,进行补偿。