1.微服务架构中的分布式事务问题
(1)传统的单体应用,假设一个单体应用的业务由3个模块构成,三者使用单个本地数据源
(2)这样的话本地事务很自然就可以保证数据一致性
(3)在微服务架构中就不这么简单了
<1>3个模块被设计为3个不同数据源之上的3个服务,每个服务对应一个数据库
<2>本地事务当然也可以保证每个服务中的数据一致性
<3>扩展到整个应用、整个业务逻辑范围来看,不同的服务保证都成功,需要分布式事务
2.分布式事务概念
(1)分布式事务是指会涉及到操作多个数据库的事务,需要保证事务的 AICD 特性
(2)其实就是将对同一库事务的概念扩大到了对多个库的事务
(3)目的是为了保证分布式系统中的数据一致性
(4)分布式事务处理的关键是:
<1>必须有一种方法可以知道事务在任何地方所做的所有动作
<2>提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)
(5)比较著名的分布式事务解决方案有:
<1>2pc(两段式提交)
<2>TCC(Try、Confirm、Cancel)
<3>本地消息表(异步确保)
<4>MQ事务消息
<5>开源的事务中间件,如LCN、GTS
3.分布式事务的解决方案
(1)两阶段提交(2PC):可以说是分布式事务的最开始的样子了
<1>通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务
1.1.这里的协调者可以是:消息中间件或者全部事务管理
<2>第一阶段:准备阶段(投票阶段) 和第二阶段:提交阶段(执行阶段)
2.1.准备阶段:
2.1.1.协调者询问参与者事务是否执行成功,参与者发回事务执行结果
2.1.2.在准备阶段,参与者执行了事务,但是还未提交
2.2. 提交阶段:
2.2.1.如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务
2.2.2.否则,协调者发送通知让参与者回滚事务
2.2.3.只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚
<6>两阶段提交(2PC)的不足:
6.1.同步阻塞:
6.1.1.所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作
6.2.单点问题:
6.2.1.协调者在2PC中起到非常大的作用,发生故障将会造成很大影响
6.2.2.特别在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作
6.3.数据不一致:
6.3.1.在阶段二,如果协调者只发送了部分Commit消息
6.3.2.此时网络发生异常,那么只有部分参与者接收到 Commit 消息
6.3.3.也就是说只有部分参与者提交了事务,使得系统数据不一致
6.4.太过保守:
6.4.1.任意一个节点失败就会导致整个事务失败,没有完善的容错机制
(2)补偿事务(TCC)
<1>TCC 其实就是采用的补偿机制
<2>核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作
2.1.它分为三个阶段:
2.1.1.Try 阶段:主要是对业务系统做检测及资源预留
2.1.2.Confirm阶段:主要是对业务系统做确认提交,只要Try成功,Confirm一定成功
2.1.3.Cancel阶段:主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放
2.2.举个例子:以转账为例
2.2.1.A要向B转账,思路大概是:有一个本地方法,里面依次调用
2.2.2.首先在Try阶段,要先调用远程接口把A和B的钱给冻结起来
2.2.3.在Confirm阶段,执行远程调用的转账的操作,转账成功进行解冻
2.2.4.如果第2步执行成功,那么转账成功
2.2.5.如果第二步执行失败,则调用远程冻结接口对应的解冻方法(Cancel)
<3>补偿事务(TCC)的优缺点:
3.1.跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC要差一些
3.2. 缺点还是比较明显的,因为网络原因在2,3步中都有可能失败
3.3.TCC属于应用层的一种补偿方式,所以需要在实现的时候多写很多补偿的代码
3.4.在一些场景中,一些业务流程可能用TCC不太好定义及处理
(3)本地消息表(异步确保)
<1>本地消息表与业务数据表处于同一个数据库中
1.1.这样就能利用本地事务来保证在对这两个表的操作满足事务特性
1.2.使用了消息队列来保证最终一致性
<2>执行流程如下:
2.1.在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息
2.2.本地事务能保证这个消息一定会被写入本地消息表中
2.3.之后将本地消息表中的消息转发到 Kafka 等消息队列中
2.4.如果转发成功则将消息从本地消息表中删除,否则继续重新转发
2.5.在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作
<3>本地消息表(异步确保)的优缺点
3.1.优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性
3.2.缺点:消息表会耦合到业务系统中,若没有封装好的解决方案,会有很多杂活需要处理
(4)MQ事务消息
<1>有一些第三方的MQ是支持事务消息的,比如RocketMQ
1.1.他们支持事务消息的方式也是类似于采用的二阶段提交
1.2.市面上一些主流的MQ都是不支持事务消息,比如RabbitMQ和Kafka都不支持
<2>以阿里的RocketMQ中间件为例,其思路大致如下:
2.1.第一阶段Prepared消息,会拿到消息的地址
2.2.第二阶段执行本地事务
2.3.第三阶段通过第一阶段拿到的地址去访问消息,并修改状态
2.4.在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息
2.4.1.如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息
2.4.2.这时候发现了Prepared消息,它会向消息发送者确认
2.4.3.所以生产方需要实现一个check接口
2.4.4.RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息
2.4.5.这样就保证了消息发送与本地事务同时成功或同时失败
<3>MQ事务消息优缺点
3.1.优点:实现了最终一致性,不需要依赖本地数据库事务
3.2.缺点:实现难度大,主流MQ不支持,RocketMQ事务消息部分代码也未开源
(5)分布式事务中间件解决方案
<1>分布式事务中间件其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果
<2>典型代表有:阿里的GTS(https://www.aliyun.com/aliware/txc)、开源应用LCN
参考文档:
https://www.cnblogs.com/mayundalao/p/11798502.html
https://www.cnblogs.com/jing99/p/11769093.html