整理下分布式事务的解决方案
2PC2PC(two-phase commit protocol)即两阶段提交.2pc是一个非常经典的强一致、中心化的原子提交协议。
中心化的意思就是,皇权集中于一人之手,其它的都是参与者.比如kafka集群.所有的信息都放置在zookeeper上,kafka只是提供服务. 同理2PC中有个协调者,用于管理所有的参与了分布式事务的应用程序cuiyaonan2000@163.com
二阶段提交是一种思想,基于该思想,有XA协议和TCC协议
2PC注释- 2PC即两阶段提交协议,是将整个事务流程分为两个阶段:准备阶段(Prepare phase)、提交阶段(commit phase)
- 2是指两阶段
- P是指准备阶段
- C是提交阶段
- 准备阶段: 协调组询问所有参与者是否准备好了。对于数据库来说,准备操作是在重做日志中记录全部事务提交操作所要做的内容,它与本地事务中真正提交的区别只是暂不写入最后一条 Commit Record。这意味着在做完数据持久化后并不会立即释放隔离性,也就是仍继续持有锁,维持数据对其他非事务内观察者的隔离状态
- 执行阶段:如果所有参与者都回复准备好了,协调者就向所有参与者发送执行命令;如果有一个参与者回复没准备好,协调者就向所有参与者发送撤销命令。--------这里其实有个漏洞,即及时程序都已经执行完毕,只剩下commit.但是当commit爆出异常怎么处理?cuiyaonan2000@163.com
- 单点故障问题,协调者挂了,尤其在第二阶段的时候参与者资源都锁着时候影响更大。
- 同步阻塞,所有节点在执行时是同步阻塞的
- 数据不一致问题,例如网络分区,部分节点接受不到提交的请求。或是当协调者在第二阶段发送提交命令后挂了,此时有个节点接受到命令执行后也挂了。其他节点都没接收过命令。如果通过选择得出新的协调者,上线后该提交还是回滚都有可能和之前挂了的节点不一致。
3PC 虽然试图解决 2PC 的问题,但它的通讯开销更大,在网络分区时也无法很好地工作,很少在工程实践中使用
XA(Transaction Manager 和Resources Manager通信协议)XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准。目前,Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议采用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。XA协议包括两套函数,以xa_开头的及以ax_开头的。
根据如上的内容我们得知: XA划分了2个角色
- 事务管理器(Transaction Manager)
- 资源管理器(Local Resource Manager)
在XA规范中,数据库充当RM角色,应用需要充当TM的角色,即生成全局的txId(这个txld在我们的程序中需要定义cuiyaonan2000@163.com),调用XAResource接口,把多个本地事务协调为全局统一的分布式事务。
XA只是一个协议和规范,它只是定义了Transaction Manager 和 Resources Manager 之间连接的一系列的接口,具体接口的实现有厂商和应用程序去实现.所以它的特点如下cuiyaonan2000@163.com:
-
性能(阻塞性协议,增加响应时间、锁时间、死锁);
-
数据库支持完善度(MySQL 5.7之前都有缺陷);
-
协调者依赖独立的J2EE中间件(早期重量级Weblogic、Jboss,后期轻量级Atomikos、Narayana和Bitronix,);-----这个有些尴尬,在几年前只有Jboss和Weblogic,后期才出现了像springboot上使用Atomikos这些东西cuiyaonan2000@163.com,让我们摆脱了对容器的依赖.
-
运维复杂,DBA缺少这方面经验;
-
并不是所有资源都支持XA协议;
TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母,含义如下:
- Try 检查及预留业务资源完成提交事务前的检查,并预留好资源。
- Confirm 确定执行业务操作,对try阶段预留的资源正式执行。
- Cancel 取消执行业务操作,对try阶段预留的资源释放。
TCC 本质上是补偿事务,它的核心思想是针对每个操作都要注册一个与其对应的确认操作和补偿操作(也就是撤销操作),所以TCC会强调幂等性
特点- XA是从数据库层面或者说数据层面来解决分布式事务,而TCC是从代码层面(就是我们的service层,或者不同的微服务)这个层面来解决分布式事务.
- 因为TCC是通过代码层来解决,所以我们的业务代码中不可避免的会被TCC的处理逻辑代码所侵入.因此,这种模式并不能很好地被复用。
消息型事务主要是在设计逻辑上解决的一个方案.消息事务需要用到消息中间件.那消息中间件该怎么选择呢~
首先我们可以知道分布式事务用于实时的,对可靠性要求较高的消息传递上.所以我们排除了kafka,使用rabbitmq.
另外在消息事务的处理过程中,我们肯定会用到RabbitMq的回调机制.其实也主要是这个回调机制来去确认消息的是否成功提交cuiyaonan2000@163.com
根据业务的不同我们的消息队列的分布式事务处理方案的逻辑也是不同的.如下图是一种场景.
当然还有更多的场景,比如:支付宝A想支付宝B转账.那业务场景就是只要A扣款成功提交,则只需要发送一个安全的消息给B,让B去增加对应的金额.如果B不存在,该消息未被成功消费,则B服务区回调A服务的接口,将钱加回去.
另外在为服务中我们的事务参与者可能有多个,通过消息中间件的分布式事务的处理方案可能更复杂cuiyaonan2000@163.com
SeataSeata 是阿里开源的分布式事务框架,属于二阶段提交模式。官网:https://seata.io/zh-cn/
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各BU业务进行了有力的支撑。经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备。
功能特色通过介绍可以很明显地感觉到Seata是整合了XA和消息型事务的思路.很多节点名称都是沿用了之前的称呼如:Transaction Manager 和 Resource Manager.而这个消息队列则Transaction Coordinato.是不是~~~cuiyaonan2000@163.com
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
- 仓储服务:对给定的商品扣除仓储数量。
- 订单服务:根据采购需求创建订单。
- 帐户服务:从用户帐户中扣除余额。
- Storage 会收到 XID,知道自己的事务属于这个全局事务。Storage 执行自己的业务逻辑,操作本地数据库。
- Storage 会把自己的事务注册到 TC,作为这个 XID 下面的一个分支事务,并且把自己的事务执行结果也告诉 TC。此时 Storage 的角色是 RM(资源管理者),资源是指本地数据库。
- Order、Account 的执行逻辑与 Storage 一致。
- 在各个微服务都执行完成后,TC 可以知道 XID 下各个分支事务的执行结果,TM(Business) 也就知道了。
- Business 如果发现各个微服务的本地事务都执行成功了,就请求 TC 对这个 XID 提交,否则回滚。
- TC 收到请求后,向 XID 下的所有分支事务发起相应请求。
- 各个微服务收到 TC 的请求后,执行相应指令,并把执行结果上报 TC。
Seata 有一个重要的机制:回滚日志。
每个分支事务对应的数据库中都需要有一个回滚日志表 UNDO_LOG,在真正修改数据库记录之前,都会先记录修改前的记录值,以便之后回滚。
在收到回滚请求后,就会根据 UNDO_LOG 生成回滚操作的 SQL 语句来执行。
如果收到的是提交请求,就把 UNDO_LOG 中的相应记录删除掉。
RM 是怎么自动和 TC 交互的?是通过监控拦截JDBC实现的,例如监控到开启本地事务了,就会自动向 TC 注册、生成回滚日志、向 TC 汇报执行结果。
二阶段回滚失败怎么办?例如 TC 命令各个 RM 回滚的时候,有一个微服务挂掉了,那么所有正常的微服务也都不会执行回滚,当这个微服务重新正常运行后,TC 会重新执行全局回滚。