分布式事务与 Seate 框架:分布式事务理论,Java 知识总结
第一阶段:CanCommit(询问阶段):事务协调者向参与者发送事务执行请求,询问是否可以完成指令,参与者只需要回答是或者不是,会有超时机制;
** 第二阶段:PreComiit(准备阶段)**:事务协调者会根据参与者的反馈结果决定是否继续执行,如果在第一个阶段 CanCommit 都收到了请求的话,就开始执行写 redo 和 undo 日志,并且返回 ACK 给协调者,这里即是二阶段提交的第一阶段。
第三阶段:DoCommit(提交或回滚阶段):如果在第二阶段 PreCommit 提交成功以后,那么事务协调者会向所有的参与者发起事务提交指令,如果其中某个参与者返回失败,则执行终止指令回滚事务。
相比较二阶段提交协议,三阶段提交协议有以下不同:
1)增加了 CanCommit 阶段:可以尽早发现参与者无法执行的情况,及时中断;
2)增加了超时机制:参与者与事务协调者都引入了超时机制,一旦超时,事务协调者和参与者会继续提交事务,并且任务处于成功状态(因为在这种情况下事务默认为成功的可能性比较大),事实上,第三阶段提交协议下仍然可能出现不一致的情况(但是概率很小)
**(
1)CAP 定理**
CAP 定理,又称布鲁尔定理,简单来说就是指在分布式系统中不可能同时满足一致性(C:Consistency)、可用性(A:Availability)、分区容错性(P: Partition Tolerance)
C:数据在多个副本中保持一致,比如前面说的分布式数据一致性问题
A:系统对外提供的服务必须一直处于可用状态;
P:在分布式中遇到任何网络分区故障,系统仍然能够正常对外提供服务。
CAP 定理证明,在分布式系统中,要么满足 CP、要么满足 AP。不可能实现 CAP 或者 CA,原因是网络通信并不是绝对可靠的。而在分布式系统中即便出现网络故障也需要保证系统仍然能够正常对外提供服务,Partition Tolerance 是必然存在的,所以 P 是一定会有的,只有 C 与 A 不能同时兼得。
AP :相当于放弃了强一致性,实现最终的一致性,这是很多解决分布式数据一致性的最终选择。
CP:放弃了高可用性,实现强一致性,之前的 2PC 与 3PC 都采用这种方案,可能会导致用户完成某个操作会等待较长时间。
(2)BASE 理论
BASE 理论是由于 CAP 中一致性和可用性不可兼得而衍生出来的一种新思想,BASE 理论的核心思想通过牺牲数据的强一致性获得高可用性。
** Basically?Available** **(基本可用):**分布式系统出现故障时,允许损失一部分功能的可用性,保证核心功能的可用。
** Soft State** **(软状态):**允许系统中的数据存在中间状态,这个状态不影响系统的可用性,也就是系统中不同节点的数据副本之间的同步存在延时
** Eventually Consistent** **(最终一致性):**中间状态的数据经过一段时间之后,会达到最终的数据一致性。
** 也就是 BASE 理论并没有要求数据的强一致性,而是允许在一段时间是不一致的,但最终数据会在某个时间点实现一致**。比如:用户发起订单支付,不需要同步等待支付的执行结果,系统会返回一个支付处理中的状态到用户界面,最后可以通过订单详情查看到支付处理结果,而对于系统来说,当第三方支付处理成功之后,再更新订单的状态即可。
===================================================================================
TCC 是一种比较成熟的分布式数据一致性解决方案,实际上是把一个完整业务拆分为三个步骤:
** Try:主要是数据的校验或者资源的预留;**
** Confirm:确认真正执行的任务,只操作 Try 阶段预留的资源;**
** Cancel:取消执行,释放 Try 阶段预留的资源。**
其实 TCC 是 2PC 的思想,第一阶段通过 Try 进行准备工作,第二阶段 Confirm/Cancel 表示 Try 阶段操作的确认和回滚。
比如:用户通过账户余额购买一个理财产品,涉及两个事件,对应两个不同的微服务中的方法:账户服务中,对用户账户余额进行扣款;理财产品中,对指定产品可申购金额进行扣减。则需要 TCC 补偿方案控制:
在账户服务中 Try 方法对余额进行冻结,Confirm 方法把 Try 方法冻结的余额进行实际扣款,Cancel 方法把 Try 方法冻结余额进行解冻;
在账户服务中 Try 方法对本次申购部分额度进行冻结,Confirm 方法把 Try 方法冻结的额度进行实际减扣,Cancel 方法把 Try 方法冻结额度进行释放;
而主业务方法中就会调用两个微服务业务中的方法(即对余额减扣、申购额度减扣),就会先调用 Try 对资源预留,如果 Try 阶段都正常,则进行 Confirm 对预留资源进行实际应用,如果不正常则 Cancel 取消对资源的预留,对资源进行回滚,从而保证数据的一致性。
而需要注意的是,微服务框架宕机或者网络异常导致没法完成 Cancel/Confirm 请求,TCC 事务框架会记录一些分布式事务的操作日志,保存分布式事务运行的各个阶段和状态,之后 TCC 事务协调器根据日志进行重试,达到数据的最终一致性。
2.基于 MQ 最终一致性方案(基于具有事务模型消息的 MQ,如 RocketMQ)
基于可靠消息的一致性是互联网公司比较常用的分布式数据一致性解决方案,比如支付服务与账户服务之间的流程需要 MQ:
但是支付服务本地事务到 MQ 发送消息存在非原子操作问题,如果先执行本地事务,再发送消息到 MQ,MQ 可能出现超时情况,导致本地事务可能回滚,从而导致数据不一致
如果先发送消息,再执行数据库事务,在这种情况下可能会出现消息发送成功但是本地事务更新失败的情况下,仍然存在数据不一致的问题
针对 MQ 与服务之间的数据不一致的情况,我们可以采用 MQ 的事务消息模型,比如 RocketMQ 为例:
生产者发送事务消息到消息队列,消息队列此时只记录消息的数据,消费者无法消费此信息;
生产者之后执行本地事务,根据执行结果发送一条确认消息给消息队列服务器,告诉消费者是否消费该消息;如果生产者本地事务执行成功则发送一条 Commit 消息,即告诉消费者可以消费该消息,否则,消息队列服务器就会删除该消息;
如果在生产者执行本地事务的过程中因为某些情况一直未给消息队列服务器发送确认,那么消息队列服务器就会主动回查生产者执行本地事务的结果,然后根据结果执行上述步骤;
消息队列服务器上存储的消息被生产者确认后,消费者就可以消费该消息,最后发送一个确认标识给消息队列服务器,表示该消息投递成功。
由上我们可知:
在 RocketMQ 事务模型中,事务是由生产者完成的,当消息没有签收的情况下,MQ 队列服务会重复投递。
RocketMQ 的事务消息模型最核心的就是事务回查(在没有收到生产的 commit/rollback 的情况下,主动查询事务状态)。
所谓的最大努力通知就是在客户端没有返回消息确认时,支付宝会不断地进行重试,直到收到一个消息确认或者达到最大重试次数。
可以参考支付宝支付的例子,如果商户不返回 SUCCESS 标识,每隔 1min、5min…会不断通知商户支付结果,达到最大次数以后就不通知,将会同时提交查询结果,定时任务出发查询。
Seata 是致力于微服务架构下提高性能和简易使用的分布式事务,它提供了 AT、TCC、Saga 和 XA 事务模式。
AT 模式是 Seata 最主推的分布式事务且基于 XA 演进而来的解决方案,主要有三个角色:TM、RM 和 TC,其中 TM 和 RM 作为 Seata 的客户端和业务集成:
TC 作为 Seata 服务器独立部署。
TM 向 TC 注册一个全局事务,并生成全局唯一的 XID;
在 AT 模式下,数据库资源被当做 RM,访问 RM 时,Seata 会对请求进行拦截;
每个本地事务提交时,RM 会向 TC(Transaction Coordinator,事务协调器)注册一个
具体步骤如下:
** 1) TM 向 TC 注册全局事务,并生成全局唯一 XID;**
** 2) RM 向 TC 注册分支事务,并将其纳入到该 XID 对应的全局事务范围。**
** 3) RM 向 TC 汇报资源的准备状态**
** 4) TC 汇总所有事务参与者的执行状态,决定分布式事务全部回滚还是提交**
** 5) TC 通知所有的 RM 提交/回滚事务**
AT 模式和 XA 类似,也是一个 2PC 模型,但实际上做了很多优化,后面再介绍
Saga 模式又称之为长事务解决方案,核心思想是:把一个业务流程中的长事务拆分成多个本地短事务,业务流程中每个参与者事务执行失败,则通过补偿机制前面已经成功的参与者。
按照 Saga 的工作模式,一般有两种方式:
1)?T1, T2,T3,T4…,Ti,表示所有事务正常运行
2)?T1, T2,T3,T4…,Tj, Cj,…,C2,C1:表示执行到 Tj 事务时出现异常,通过补充操作撤销之前的所有成功的 sub-transaction。
另外,提供两种补偿模式:一是向后补偿,即第二种方式,任一子事务执行失败,则把之前的子事务结果逐一撤销。另一种就是向前恢复,都可以出现失败情况,在最坏的情况下只能进行人工干预处理。
(1)Saga 优劣势
优势:
一阶段直接提交本地事务(相比较 XA/TCC 模式没有 Try);
没有锁等待,性能比较高;
在事件驱动模式下短事务可以异步执行;
补偿机制实现比较简单。
评论