分布式事务常见解决方案

分布式事务常见解决方案

一、事务介绍

事务是一系列的动作,它们综合在一起才是一个完的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。

1、单事务概念

应用多次数据库操作,通过用事务进行管理,来保证ACID原则。

  • 原子性(A):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态;

  • 一致性(C):事务的执行使数据从一个状态转换为另一个状态,事务在执行之前和之后,数据库都必须处于一致性状态。

  • 隔离性(I):在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据;

  • 持久性(D):当事务正确完成后,它对于数据的改变是永久性的。

2、分布式事务概念

分布式事务常见场景:

  1. 单应用内部调用(多个数据源调用,操作多个库)

  2. 涉及多应用调用(有可能操作同一个数据源,也有可能操作不同的数据源)

CAP理论

分布式事务的理论基础(ACID事务无法满足)

  • C:一致性 数据一致性:强一致性、弱一致性、最终一致性 强一致性:流程涉及的各个环节数据必须实时一致性 弱一致性:流程涉及的各个环节数据允许存在部分数据不一致 最终一致性:允许存在中间状态,只要求经过一段时间后,数据最终是一致的

  • A:可用性 系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果

  • P:分区容错性 (一定会存在) 分布式系统在遇到任何网络分区故障时,仍然需要能够保证对外提供满足一致性和可用性的服务

常见组合: AP:互联网业务 CP:金融业务

base理论

base理论是CAP理论中AP方案的延伸,核心思想是即时无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

  • Basically Available (基本可用)

  • Soft state (软状态,中间状态)

  • Eventually consistent (最终一致性)

更详细的介绍见 分布式系统原理

二、分布式事务常见方案

分布式场景下,多个服务同时对服务一个流程,比如电商下单场景,需要支付服务进行支付、库存服务扣减库存、订单服务进行订单生成、物流服务更新物流信息等。如果某一个服务执行失败,或者网络不通引起的请求丢失,那么整个系统可能出现数据不一致的原因。

常见方案

  • 1、设计方案尽可能规避分布式事务方案(相似的业务放在一起,不要过度拆分)

  • 2、强事务(CP,低并发短事务)和柔性事务(AP,高性能)

强事务:满足CP理论,XA协议(2PC、JTA、JTS)

  • 3PC:但由于同步阻塞,处理效率低,适合低并发、短事务业务.

  • 2PC:Seeta(AT)、LCN(2PC),适合分布式系统

  • JTA: atomikos(适合单系统多数据源)

柔性事务:满足AP,base理论,适合异步更新数据,并且对数据的实时性要求较低的场景,主要分为:

  • 补偿型 (TCC、saga)

  • 最大努力通知型(MQ、本地消息表)

  • 异步确保型(MQ、本地消息表)

实现方式

  • TCC(seeta-tcc,lcn-tc)

  • Saga (seeta-saga状态机模式、Aop模式)

  • 本地事务消息

  • 事务消息MQ

互联网业务,一般的流量比较大,涉及很多高并发场景、我们一般采用柔性事务,这样系统的性能好。

三、柔性事务之最大努力通知型(互联网应用最广泛)

基于本地事务消息表实现分布式事务

image.png

基于本地事务消息表+MQ实现柔性分布式事务

方案一:主业务中发送MQ消息

image.png

重试注意事项

  1. 通过本地消息表+MQ重试对账+下游(接口幂等、提供);

  2. 打印日志+告警+人工介入补偿

回滚注意事项

  1. 程序捕获异常,调用回滚代码;

  2. 发送回滚MQ,各个系统消费MQ,调用本地回滚方法。

方案一:独立JOB发送MQ消息

image.png

1、我们把主业务数据表和待发送的消息存储表共存于同一个数据库(上图第一步),便于数据写入的时候由一个事务保证,数据写入成功后(上图第二步),事务即成功。
2、此时,需要一个定时任务JOB(上图第三步)来执行对消息表里的消息进行发送到MQ(上图第四步),消息发送MQ成功后(上图第五步),删除消息表里的消息(上图第六步),达到异步处理的目的。

比如:还是下单操作,下单完成后,需要通知库存扣减库存,财务进行收款,仓库进行发货等操作,设计时,可以把订单表与订单消息表共同存储到一个数据库里,当订单表写入的时候也同步写订单消息表,2个表同时写成功后,事务才算成功。然后再通过一个任务来查询订单消息表把订单消息表里的消息发送到MQ,当收到MQ的成功回执消息后,再把消息表里的消息进行删除。库存扣减、财务收款、仓库发货等均消费MQ里的该消息来完成各自的业务操作

在该方案模型中,当MQ发送方把消息发送后,长时间未收到MQ服务端的结果回执,此时需要再次发送该消息,这导致消息的发送可能会重复多次发送,需要消费方保证处理消息的接口幂等;该方案的优点是对业务的侵入小,核心业务只需要关注把自己业务做完的同时把需要发送的消息写入消息表即可。该方案也是在我们生产中用的比较多的方案之一。

方案一 VS 方案二

方案二和方案一的主要区别,就是这个方案解耦就加彻底,主业务直接把自己的业务数据和待发送的消息写入数据库后就完结了,另起一个JOB专门处理消息读取和发送。

上一个方案,是在主业务里发送消息失败后,才写入本地消息表。由于需要同步发送消息,所以在高并发下,发送MQ可能成为系统瓶颈。

方案二明细并发量会更高,但是消息表数量会很大。

在用消息队列处理的业务场景中,都存在发送方重复多次发送的可能,所以消费方都需要保证业务接口的幂等性,便于重复发送,重复消费时业务的幂等。消息队列在电商、互联网等业务中用的比较广泛,其作用主要体现在业务消峰,解耦业务,异步处理等场合。

基于RocketMQ实现柔性分布式事务

image.png

1、本地事务执行前,把需要发送的mq消息先发送到MQ服务器上,但该消息属于不能投递的消息,需要有标识标明;
2/3、当MQ发送方收到MQ服务器返回的收到发送的mq消息的确认后,执行本地事务;
4、根据本地事务的执行结果再向MQ的服务器发送该消息的本地事务执行结果;
5、当MQ服务端(MQ server)收到消息后,会把mq消息投递到消费方还是把mq消息回滚丢弃;
7/8、当MQ服务端(MQ server)超时未收到MQ发送端对mq消息的处理通知(Commit OR Rollback)时,MQ服务端会向MQ发送方进行查询该mq消息的事务状态,以确定该mq消息的处理结果是投递还是丢弃。

缺点:该方案需要业务方针对每一个事务提供一个回查接口给MQ服务端;同时,MQ服务端还需要有定时任务来检查未投递的消息,并计算消息是否已经超时,如果已经超时需要回查业务方提供的回查事务状态接口以便确认对该消息的处理;该方案对业务的侵入比较大,不利于方案的扩展。目前支持该模式的消息队列有Apache RocketMQ。

除非是公司已有现成的技术栈或者历史功能,否则不应该使用这种方案。对业务代码侵入太大了。目前主流推荐方案还是 本地事务消息表+MQ。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容