总图:
1 基础理论部分
1.1 CAP
- C:一致性,区分为三个等级:强一致性、弱一致性与最终一致性。
- A:可用性,在有限时间内返回正常结果
- P: 分区容错性,在某分区出现故障后,仍能对外提供一致性和可用性的服务 三者最多同时满足两项,在大多数应用场景中,都需要牺牲强一致性来换取系统的高可用性。
1.2 BASE理论
根据CAP定理演化而来,核心思想在于急事无法做到强一致性,但每个业务也可以根据自身业务特点,采用适当的方法来达到最终一致性
- Bascially Available 基本可用:在系统发生不可预知的故障的时候,允许损失部分可用性,但是不等于系统完全不可用
- Soft state 软状态 :允许系统中的数据处于中间状态,并且认为中间状态的存在不会影响系统的整体可用性
- Eventually consistent :系统中所有的数据副本,在经过一段时间后,都能打到最终一致的状态。
2 刚性事务
2.1 XA规范
- X/OPEN国际联盟有限公司,它的建立时为了向UNIX环境提供标准,促进对UNIX语言、接口、网络和应用的开放式系统协议的制定。
- X/OPEN DTP(Distributed Transaction Process)是一个分布式事务模型,主要使用两段提交(2PC)来保证分布式事务的完整性。
- XA规范是X/OPEN组织定义的分布式事务处理标准,规范了全局的事务管理器和资源管理器之间的接口,它的目的是允许多个资源可以在同一事务中访问,可以使ACID属性跨越应用程序的同时保证有效。
- XA是数据库的分布式事务,具有强一致性,在整个过程中,TM始终把持着数据库的锁,如果期间其他线程要修改数据库中该条数据,就必须等待锁的释放,存在着长事务的风险。
XA有两大限制:必须拿到所有的数据源,并且数据源还要支持XA协议;性能比较差,需要锁住所有涉及到的数据,会产生长事务。
2.2 Seata AT 模式
增强版2PC模式,将事务分为两个阶段:一阶段将业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和链接资源;二阶段 提交异步化,非常快速地完成,或回滚第一阶段的回滚日志进行反向补偿。
2.3 LCN(2PC)
TX-LCN是一款事务协调性框架,本身并不生产事务,而是本地事务的协调者,从而达到事务一致性的效果。
顾名思义,2PC需要分两个阶段进行处理:阶段一提交事务请求,阶段2 执行事务提交。若阶段1 出现超时或异常,则阶段二中断事务。
- 阶段1
提交事务请求:1、事务查询。协调者向所有参与者发送事务内容,询问是否可以执行提交操作,并开始等待各参与者进行响应;2、执行事务:各参与者节点执行事务操作,并将undo和redo操作计入本机事务日志;3、各参与者向协调者反馈事务问询的响应,成功执行返回yes,否则返回no
- 阶段2:执行事务提交
协调者决定是否最终执行事务的提交操作,若所有的执行者都返回yes,则执行事务提交:发送提交请求 -> 事务提交,并释放期间占用的资源 -> 反馈提交结果,向协调者发送ACK消息确认 -> 完成事务,协调者收到所有参与者的ACK消息后,最终完成事务
- 阶段二:中断事务
当某一个参与者向协调者发送no响应,或者等待超时,则中断事务。1、发送回滚请求,向所有的参与者发送RollBack请求 2、回滚,参与者收到信息后,利用本机的undo信息,执行回滚操作,并释放资源 3、反馈回滚结果 4、中断事务
2.3.1 2PC的优缺点
优点在于实现原理简单,但是缺点比较多
- 在事务的执行过程中,所有的参与事务操作的逻辑都处于阻塞状态
- 协调者是个单点,若出问题,其他参与者无法释放事务资源,也无法完成事务操作
- 数据不一致,在执行事务提交的过程中,若某条消息丢失掉,则整个系统将出现数据不一致的情形
- 保守,没有完善的容错机制,一旦出现问题,只能回滚
2.4 3PC
3PC将2PC的过程扩展为了三个过程:
- 阶段一 : CanCommit
事务询问,询问各个参与者是否可以执行事务提交,并等待应答 -> 各参与者反馈事务询问,返回YES / NO
-
阶段二 :PreCommit 执行事务预提交 发送预提交请求 -> 事务预提交 -> 各参与者成功执行事务操作,并将反馈以ACK响应形式发送给协调者,等待最终的Commit或Abort命令。
-
阶段二: PreCommit 中断事务
任意一个参与者发送了NO请求,或者等待超时了 协调者就 发送中断请求,并中断事务。
-
阶段三:doCommit 执行提交 当协调者收到所有参与者的ACK响应后,将预提交状态转化为提交状态,并向所有的参与者发送doCommit请求;参与者收到后,执行正式的提交请求,并释放资源,向协调者发送ACK响应; 协调者收到所有的ACK响应后,最终完成事务
-
阶段三:doCommit 中断事务 若在该阶段,收到任意一个no响应,或者等待超时,则中断事务。协调者向所有的参与者发送abort,参与者收到后,利用undo回滚消息,释放资源;回滚完毕后,向协调者发送ack响应;协调者收到所有的ack消息后,完成事务中断。
3PC 相对于 2PC优化的地方
- 3PC对协调者和参与者都设置了一个超时时间,避免了参与者长时间无法与协调者节点通讯的情况下,无法释放资源的情况。另外,多设置了一个缓冲阶段,进一步保证在最终提交阶段之前各参与节点的状态一致。
3 柔性事务
柔性事务具有两大特性:基本可用 和 柔性状态;主要分为两种:补偿性和通知型
3.1 通知型事务
通知型事务主要是通过MQ来通知其他事务参与者自己事务的执行状态,各参与者异步执行,所以也被称为异步事务。主要用于需要异步更新数据,并对数据的实时性要求不高的场景。
- 异步确保型事务:主要适用于内部系统的数据最终一致性保障,如订单和购物车、收货与结算等
- 最大努力通知:主要用于外部系统,因为外部的网络环境更加复杂和不可信,只能尽最大努力去通知,实现数据最终一致性,如充值平台与运营商等。
3.2 异步确保型事务
3.2.1 半步消息机制来实现异步事务
- 半消息机制:在原有队列消息执行后的逻辑,如果后面的本地逻辑出错,则不发送消息,如果通过,则告知MQ发送。 依靠MQ的半消息机制来实现投递消息和参与者自身本地事务的一致性保障,借鉴了2PC的思路,可以看作是它的广义拓展。
- 事务发起方首先发送半消息到MQ
- MQ通知发起方信息发送成功
- 半消息发送成功后执行本地事务
- 根据本地事务的执行结果返回commit或rollback
- 如果rollback,回滚,如果commit,MQ发送消息给消息订阅方
- 订阅方根据消息执行本地事务
- 本地事务执行成功,在MQ中将消息标记为已消费
- 如果在执行本地事务的过程中,执行端挂掉或超时,MQ服务器将不停询问消息生产者来获取事务状态
- Consumer端的消息成功机制由MQ保证。
3.2.2 基于阿里的RocketMQ实现MQ异步确保型事务
常见的消息中件键中,ActiveMQ 与 RocketMQ支持事务,但是Kafka 与 RabbitMQ 不支持异步事务。
在这里大致描述一下整体的过程: 1. 1. 1. ……
3.3本地消息表方案
当目前使用的MQ组件不支持分布式事务的时候,就得找到一个尽量少的侵入业务方,即 基于DB本地消息表 ,它的核心思想就是把分布式事务拆分为本地事务进行处理。
3.3.1 消息发送方
- 需要一个消息表,记录消息状态信息
- 业务数据和消息表同处于一个数据库中,利用本地事务来将业务数据和消息数据直接写入数据库
- 本地事务处理完消息数据和业务数据后,通过写消息到MQ消息队列,使用专门的投递工作线程将事务消息传递给MQ,根据返回的ACK删除事务消息表数据
- 消息发送到消费方,若失败,则重试
3.3.2 消息消费方
- 处理消息队列中的消息,完成自己的业务逻辑
- 若本地事务处理成功,则表示已经处理成功了
- 若本地事务失败,则重新执行
- 如果是业务层面的失败,给消息生产方发送一个业务补偿信息,通知进行回滚等操作。
3.3.3 本地消息表的优缺点
优点
- 建设成本低,实现了可靠消息的传递,确保了分布式事务的最终一致性
- 无需提供回查操作,进一步减少业务的侵入
- 在某些场景下,可以进一步利用注释等形式进行解耦,实现无业务代码侵入的实现
缺点
- 本地消息表与业务耦合在一起,难于做成通用性,不可独立伸缩
- 本地消息表基于数据库来实现,因此在高并发下,具有性能瓶颈问题
3.4 最大努力通知
通知型事务的难点在于:投递消息和参与者本地事务的一致性保障 核心都是为了保证消息的一致性投递,所以它的解决办法也有两个分支:基于MQ消息队列实现、基于DB本地事务消息表实现。
3.4.1 MQ事务消息方案实现最大努力通知
特性
- 业务主动方完成业务处理后,向业务被动方发送消息,允许存在消息丢失
- 业务主动方提供递增挡位时间间隔,若发送失败则重试,通知N次后,报警、记录日志、人工干预
- 业务被动方提供幂等性的服务接口,防止消息的重复消费
- 业务主动方需要有定期校验机制,对业务数据兜底,确保数据的最终一致性
- 被动方的结果不影响主动官方的处理结果
- 适用于对业务最终一致性的时间敏感度较第的系统
3.4.2 本地消息表方案实现最大努力通知
多加一个步骤:生产方定时扫描本地消息表,把还没处理的消息或失败的消息再发送一遍。 特点 : 与上相同
3.5 最大努力通知事务与异步确保型事务的区别
- 从业务适合度上讲,异步确保型事务适合于同网络体系的内部服务支付,而最大努力通知事务,适合于跨平台、跨企业的系统间使用
- 从消息层面来将,异步确保型事务只需要消息消费者主动去消费就可以了 ,但是最大努力通知事务需要主动推送,并提供多挡次时间的重试机制来保证消息数据的通知
- 从数据的层面,异步确保型事务只需要保证消息的可靠传递即可;但最大努力通知事务需要额外的定期校验机制对数据进行兜底,保证数据的最终一致性。
4 补偿型模式实现
通知型业务不能解决所有的业务场景,所以引入了补偿型的业务:使用一个额外的协调服务来协调各个需要保证一致性的业务服务,协调服务按照顺序调用各个业务微服务,若某一个出现异常,则取消之前所有已经调用成功的业务。 只要有两种实现模式:TCC 和 Saga
4.1 TCC分布式事务模型
- 主业务服务:整个业务的发起方,服务的编排者,负责发起并完成整个业务服务
- 从业务服务:整个业务的参与方,负责提供TCC业务操作:初步操作Try ,确认操作Confirm , 取消操作Cancel ,供主业务服务调用。
- 业务活动管理器:业务活动管理器管理控制整个业务活动,包括记录维护TCC全局事务的事务状态和每个从业务服务的子事务状态,并在业务活动提交时调用所有从业务的Confirm操作,再业务活动取消时调用所有从业务的cancel操作。
4.2 TCC事务模型的要求:
- 1、可查询:服务操作具有全局唯一的标识,操作唯一的确定的时间
- 2、幂等操作
- 3、TCC操作: Try阶段,尝试执行业务,完成所有业务的检查,实现一致性,并预留必须的业务资源,实现隔离性;confirm阶段,真正去执行业务,不做任何检查,还得满足幂等性;cancel操作,取消执行业务,释放try阶段预留的业务资源,也需要满足幂等性。
- 4、可补偿操作:Do阶段,真正执行业务处理,处理结果外部可见;Compensate阶段:抵消或者部分撤销正向业务操作的业务结果,补偿操作满足幂等性。约束:补偿操作在业务上可行,由于业务执行结果未隔离,或者补偿不完整带来的风险和成本可控。
4.3 TCC 与 2PC的不同之处
- XA是资源层面上的分布式事务,强一致性,在整个提交过程中,一直持有资源的锁。基于数据库支持XA协议,由于在执行事务的全程都需要对相关数据加锁,所以高并发情况下性能较差
- TCC是业务层面上的分布式事务,只保证了最终一致性,不会一直持有资源的所 ,性能较好,但是侵入性强,每个事务都得实现TCC三个方法,开发、维护成本高。且必须实现幂等性操作,必然损耗一定的性能,拉长业务的时间。
5 Sage长事务模型保证柔性一致性
Sage模型把一个分布式事务拆分为多个本地事务,每个本地事务都有自己对应的执行和补偿模块。当某个本地事务出错后有,调用相应的补偿方法恢复到之前的业务。
**牺牲了一定的隔离性和一致性,但提高了长事务的可用性**。
5.1 Sage模型的三个组成部分
- LLT(Long Live Transaction):由本地事务组成的事务链。
- 本地事务
- 补偿:每个本地事务都有对应的补偿。
5.1.1 Saga执行顺序
- T1 、 T2 、T3 、 T4 、 Tn
- T1 、 T2 、T3 、 T4 、 Tn 、 C3 、 C2 、 C1
5.1.2 Saga两种恢复策略
- 向后恢复 , 撤销掉之前成功的所有成功子事务,如果任意本地子事务失败,则补偿已完成的事务
- 向前恢复 , 重试失败的事务,适用于必须得成功的业务。若失败,则一直重试,真不行的话,就人工干预
5.1.3 Sage的使用条件
- 只允许两个层次的嵌套,顶级Saga和简单子事务
- 在外层的全原子性不能得到满足,也就是Sagas可能会看到其他的sagas的部分结果
- 每个子事务应该是独立的原子行为
- 补偿事务从语义角度撤销了事务Ti的行为,但未必能将数据库返回到执行Ti时的状态。
5.2 Saga模型的解决方案
- 半消息模式(消息队列与数据库)
- 本地消息表(向本地业务表和消息表中插入数据)
5.3 Saga与TCC对比
Saga相对于TCC,缺少预留动作,导致补偿动作的实现比较麻烦,但是减少了通讯次数,并且某些第三方服务没有Try接口,使用TCC实现的话 ,就比较麻烦;且没有预留动作,也意味着不必担心资源释放问题,异常处理更加简单。
TCC与Saga都是补偿性事务,无法保证隔离性
6 分布式解决工具 |: Seata
Seata提供了四种解决方案: AT、TCC、Saga 和 XA事务模式
6.1 Seaga AT 模式
增强型二阶段提交实现 …………(待补充)