分布式事务的几种解决方案

 

引言

在单体应用中,在同一数据源上更新数据的一致性由数据库本身的事务特性来保证。

 如图一个订单业务,如果中间某一操作失败,依赖数据库的事务可以保证数据的一致性。然而随着业务需求和架构的变化,单体应用被拆分为微服务,原来在一个应用中完成的业务被拆分到多个应用

上图中3个DB操作本应为一个事务,每一个服务内部的数据一致性仍由本地事务来保证。而整个业务层面的全局数据一致性要如何保障呢,这就是分布式事务要解决的问题。分布式事务是涉及俩个以上网络主机的数据库事务,和单机数据库事务一样,需要具有ACID(原子性、一致性、隔离性、持久性)属性,其中原子性保证一系列操作的结果是全生效或全失效。

几种解决方案

CAP原理是现代分布式系统的理论基石

C:Consistency一致性,每次读取都是最新的数据

A:Availability可用性,保证每次请求都会返回数据,即使该数据并不是最新的。

P:Partition tolerance分区容错性,节点之间网络发生故障,保证系统可用。

CAP原理:必须在可用性与一致性之间做出选择,不能二者同时满足。

当选择了一致性,那么当集群之间的节点数据未同步完成,就不能给予请求响应,必须等到数据同步完成。当选择了可用性,数据未同步完成时也能给予请求响应,只是该数据有可能是已经被修改了。CA不能同时兼得

BASE理论:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性),BASE是对CAP中一致性和可用性权衡的结果,核心思想是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。

1. XA

最早的分布式事务模型是 X/Open 国际联盟提出的 X/Open Distributed Transaction Processing(DTP)模型,简称XA 协议。

其中包含一个全局事务管理器(TM,Transaction Manager)和多个资源管理器(RM,Resource Manager)。全局事务管理器负责管理全局事务状态与参与的资源,协同资源一起提交或回滚;资源管理器则负责具体的资源操作。XA 协议描述了 TM 与 RM 之间的接口,允许多个资源在同一分布式事务中访问。XA使用两阶段提交来保证所有资源同时提交或回滚任何特定的事务。阶段一为准备(prepare)阶段。即所有的RM锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向Transaction Manager报告已准备就绪。阶段二为提交阶段(commit)。当Transaction Manager确认所有参与者都ready后,向所有参与者发送commit命令

关于二阶段提交

关于2PC与3PC这篇博文2PC与3PC总结的很好,详细的介绍可以看下这个,这里只做简单介绍。

二阶段提交即2PC,当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一控制所有节点(参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交。它将事务的提交过程分为两个阶段来进行处理:准备阶段和提交阶段。
准备阶段:协调者询问参与者是否准备好提交,准备阶段并不会释放锁资源
提交阶段:协调者首先持久化本地事务,再发送commit请求,参与者若未回复或回复失败则发送rollback指令
3个缺点:协调者单点问题(参与者等待协调者指令时无法做超时处理),性能问题,一致性问题(例如协调者commit之后断连)

三阶段提交:将准备阶段进行拆分,分为canCommit和preCommit,相当于只是增加了一轮询问操作。在事务需要回滚的情况下通常性能比两阶段要好,但正常情况下性能比2阶段要差。
解决单点问题:协调者在preCommit之后宕机,参与者默认是提交事务
一致性问题并没有解决

回到XA,现在很多的数据库都实现了XA协议,这使得我们使用分布式事务可以同本地事务一样简单,我们来看最熟悉的MySQL XA协议的相关实现。首先能够支持XA的只有InnoDB引擎,MySQL的XA又分为外部XA与内部XA。

内部XA:在开启binlog(二进制日志,它记录了数据库上的所有改变)的情况下,MySQL同时维护了binlog日志与InnoDB的redo log(新数据的份。在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo Log已经持久化,系统可以根据Redo Log的内容,将所有数据恢复到最新的状态, Redo Log 保证事务的持久性。还有一种log叫Undo Log, Undo Log的原理很简单,为了满足事务的原子性,在操作数据之前,首先将数据备份到Undo Log。然后进行数据的修改。如果出现了错误或者用户执行了  ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态, Undo Log 保证事务的持久性。)为了保证这两个日志的一致性,MySQL使用了XA事务,由于只在单机上工作,所以被称为内部XA,此时MySQL服务器中的存储引擎充当RM,而服务器本身充当TM。

外部XA:指多个MySQL实例的分布式事务,即广泛意义上的分布式事务,此时MySQL服务器作为RM,外部应用程序作为TM

JTA可以用于分布式系统中分布式事务的管理.原理是通过两阶段的提交.可以同时管理多个数据源的事务.

XA协议是一套分布式事务管理的规范,JTA是XA协议在Java中的实现,多个数据库或是消息厂商实现JTA接口,开发人员只需要调用SpringJTA接口即可实现JTA事务管理.

但是JTA也有比较严重的性能问题,由于同时操作多个数据源,如果其中一个数据源获取数据的时间过长,会导致整个请求都非常的长,因此现实中对性能要求比较高的系统较少使用JTA事务管理.

常用分布式系统事务管理实现高性能和高吞吐的方式是Spring事务同步机制以及牺牲掉事务的暂时一致性,而保证事务的最终一致性.

SpringBoot通过Atomikos或Bitronix支持JTA分布式事务,当配置了JTA时,Spring JtaTransactionManager将作为事务管理器,@Transactional注解自动支持XA事务

补偿事务TCC

TCC为以下三个英文的缩写:
try:完成所有业务检查, 预留必须业务资源
confirm:确认执行业务操作
cancel:取消执行业务操作。
其中confirm和cancel是一对反向操作,所以TCC的原理其实比较简单粗暴,如果出错,则对原来的所有操作进行回滚。其本质上为一个2阶段提交。它的缺点在于Try、Confirm和Cancel操作功能需业务提供,开发成本高。 

对于TCC,阿里的分布式事务框架Seata就采用了该方案。

本地消息表

最初由ebay架构师提出来的分布式事务解决方案,大致流程如下
1、系统A首先更新自己的业务表,并同时会在本地消息表中插入一条数据,这两个操作是在同一个事务当中完成
2、定时任务扫描本地消息表,向MQ中间件进行消息发送
3、系统B消费消息,消费成功通知A进行本地消息表状态更改,消费失败通知A进行回滚

缺点:依赖MQ的可靠性,由于MQ可能失败重试等原因,需要生产者和消费者同时支持幂等性

网图

可靠消息最终一致性

同本地消息表一样还是需要依赖MQ来实现分布式事务,基本步骤如下
1、系统A发送prepare消息到MQ
2、收到ack执行本地事务,并根据执行结果发送消息,MQ根据执行结果判断是否将prepare消息转为confirm消息
3、对于迟迟不发送本地事务执行结果的prepare消息,MQ会进行回查
4、系统B消费MQ中的消息

小米信息技术部

阿里RocketMQ基于该方案为我们提供了完整实现:

第一阶段:生产者向MQ服务器发送事务消息(prepare消息),服务端确认后回调通知生产者执行本地事务(此时消息为Prepare消息,存储于RMQ_SYS_TRANS_HALF_TOPIC队列中,不会被消费者消费)
第二阶段:生产者执行完本地事务后(业务执行完成,同时将消息唯一标记,如transactionId与该业务执行记录同时入库,方便事务回查),根据本地事务执行结果,返回Commit/Rollback/Unknow状态码
1、服务端若收到Commit状态码,则将prepare消息变为提交(正常消息,可被消费者消费)
2、收到Rollback则对消息进行回滚(丢弃消息)
3、若状态为Unknow,则等待MQ服务端定时发起消息状态回查,超过一定重试次数或者超时,消息会被丢弃

被动方服务订阅主题后只需要等待MQ投递消息即可。
当消息投递,被动方服务消费该消息并执行本地业务操作,当本地业务执行成功,被动方服务调用消息服务,返回本地业务执行成功。
可靠消息服务根据业务唯一参数(订单号结合消息id)设置消息状态为 “已完成”
整个过程中,作为被动方服务需要尽最大努力将业务向最终状态推进,最终成功或者失败并通知消息服务置消息状态为完成的终态。

最大努力通知

这个方案比较简单,适用于一些时间敏感度比较低或者分布式事务要求不太严格的业务。
1、系统A执行本地事务之后,发送消息到MQ
2、系统B消费MQ,系统B执行失败会进行重试。

关注公共号:八九的博客,帮你内推字节跳动!

6人点赞

分布式相关

 


版权声明:本文为weixin_35687480原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。