分布式事务很难?看完这篇文章你就都懂了

首先我们需要了解一下什么是分布式事务,根据官方的语言,分布式事务是指事务参与者,支持事务的服务器,资源服务器和事务管理器分别在不同的分布式系统的不同的节点中,且属于不同的应用,分布式事务需要保证他们的操作同时成功,或者同时失败。从本质上来说,就是要保证不同数据库下数据的一致性。
看了上面官方的论述,可能还是觉得有点绕,没关系,接着往下看:
1.1先简单了解一下基本概念
本地事务:
只涉及到一个数据库的事务,我们称之为本地事务,它是分布式事务的最小单位。

CAP原理:
C:consistence:一致性。就是不同数据库之间数据的一致性
A:availability:可用性。可以举个例子:Eureka的集群模式就保证了高可用,因为当一个主节点宕机之后,从节点能立马顶上去,不会导致系统停止运作。
P:Partition torelance:分区容错率。打个比方,这里集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。事实上在分布式系统当中,分区容错率都比较高,出现故障的概率特别小,所以这个是肯定能保证的。
但是C和A在分布式系统中只能保证其中的一个,比如你选择了CP,就必须牺牲A的高可用。如果你选择了AP,那么你就要牺牲C一致性。

1.2基于XA协议的两阶段提交(2PC),先上图在这里插入图片描述
先说说第一种提交成功的分布式事务:
首先事务协调者(相当于村长)发起投票,让事务的参与者(村民)投票决定是提交还是回滚,如果所有村民都决定提交了,就把投票结果告诉事务协调者(通过netty)。这就是我们上面说的准备阶段。
事务协调者在得知投票结果后,再次向事务参与者确认投票结果,发现没有问题了,就决定提交。

在这里插入图片描述
第二种情况是提交失败,需要回滚:
大致的流程跟上面是一样的,只是事务参与者中并不是全都选择了提交,有些本地事务因为异常导致执行失败,从而导致全局事务都跟着回滚。

这个模式的优点是保证了CP,因为是全部操作要么同时成功,要么同时失败。但是牺牲了A高可用,只要一个本地事务执行失败,全都跟着回滚。

1.3 TCC补偿事务:
在这里插入图片描述
从上图可以看出,总共有三个步骤,try-confirm-cancel,因而我们也称其为3阶段提交。首先业务应用启动事务,调用try接口试试服务之间的口风,是选择提交还是回滚。如果每个服务都选择提交了,这时候业务应用再调用事务协调器,选择提交,事务管理器这时候就调用confirm接口正式提交。而第二种情况就是如果各个本地服务中有回滚操作的,那么业务应用就选择回滚事务,然后事务协调器调用Cancel接口。
它的优点就是实现以及流程相对比于2PC比较简单。
缺点就是try和confirm阶段都有可能因为网络原因而失败,且需要程序员写大量的补偿代码来执行事务。
在高并发情况下任何一个事务都能对数据进行修改,无法保证数据的幂等。

1.4本地消息表
在这里插入图片描述
这种解决方案就比较高端一点,直接运用kafka消息中间件。它的大致流程是首先业务执行操作写入数据库,然后再在kafka的生产者一端写入相同的消息数据,这个消息数据也会写入到数据库DB A中,一般会创建一个本地消息表,然后将这个消息通过kafka传输到另一个事务当中,如果失败会不断重试。另一个事务通过消费者端读取数据,并执行业务操作写入DB B中,如果业务执行成功,两个事务同时提交。如果执行失败,就会通知生产者方的事务进行回滚(修改消息状态那条线就是)。
这种方案是我非常推荐大家在实际开发中运用的,因为他们实际上并没有使用分布式事务的任何东西,而是运用了消息中间件和本地消息表实现了事务的最终一致性。
他的缺点也是显而易见的,本地消息表会耦合到业务系统当中,从而导致后期会给程序员增加不少工作量。

1.5 RocketMQ事务消息
在这里插入图片描述MQ 发送方首先发送half消息,顾名思义,half就是只能传递一半路径的消息,就是只能被MQ Server收到的消息,server收到消息后告知发送方我收到啦,然后发送方执行本地事务,如果都成功了,就再发一条消息给server说我要提交,然后server在收到消息后(没有收到消息的话就自己回查事务状态,问问发送方到底啥情况?)就会向MQ订阅方发送提交消息。如果本地事务执行失败,那最后server就直接把消息删了不发给订阅方。
优点:实现了最终一致性,不需要依赖2PC。
缺点:目前主流MQ中只有RocketMQ支持事务消息。

1.6 seata解决方案
这是由我们伟大的阿里巴巴发起的分布式解决方案,一开始叫fescar,它有TCC和AT模式,这里我们只讲AT模式,为什么呢?因为TCC模式要写大量补偿代码,而AT模式类似于2PC,只需要在***业务层方法中加一个注解@GlobalTransactional即可***。
首先我们需要了解一些基本概念:
RM(resource manager):主要负责本地事务的注册,本地事务的投票并发送投票结果给TC,接收来自事务协调器(TC)的指令驱动本地事务的提交或回滚。相当于科长
TC(transaction coordinator):接收到RM的所有本地事物的投票结果之后,决定提交或者回滚,并向RM发送指令。相当于局长
TM(transaction manager):负责开启全局事务,并最终真正由他执行全局事务的提交或者回滚。也就是说前面的两个都是领导,TM才是真正干活的人,他的地位相当于科员。
在这里插入图片描述
这张图很好的向我们解释了AT模式是如何运行的,首先是本地事务在RM中注册一个账号,并获得一个xid,这个xid非常重要,他是后期分布式事务进行统一回滚的依据,存放在数据库的undo_log中。然后RM在所有本地事务执行完成后的投票结果发送给TC,TC在收到结果之后表示全员通过提交或回滚,然后将消息发送给RM。

讲的再具体一点,其实可以把它分成两个阶段来保证最终事务的一致性,第一阶段:
在这里插入图片描述
这个图已经把这个阶段的所有要点标出来了,懂的人自然就懂。在这里我稍微做一点锦上添花的讲解。
首先在需要进行事务的数据库中添加一个undo_log表,然后在我们运用seata事务的时候,会使用新的数据库连接池(ProxyDataSource),它会将业务流程写入到undo_log中。

undo_log表结构如下:

CREATE TABLE `undo_log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime(0) NOT NULL,
  `log_modified` datetime(0) NOT NULL,
  `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_unionkey`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

然后我们再接着往下讲,当我们执行了sql语句之后,undo_log会把它记录到rollback_info中,以供给后面的回滚操作运用。最后生成的行锁能够保证其他事务无法对数据进行操作,然后释放资源。以往的2PC必须在执行的两个阶段都锁定资源,现在只需要在第一阶段完事后就释放资源,是不是感觉瞬间就提高了不少效率呢!!!

第二阶段
如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志)。
在这里插入图片描述
如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值