分布式事务

一、概述(重点理解文中提到的思想而非案例)

面试时经常被问到分布式事务问题,今天我们就来谈一谈常见的分布式环境下的解决方案,大家重点理解文中提到的思想,案例并非真实案例只是为了举例说明基本原理。另外并没有什么方案和技术是最好的,要看自己的业务适合用什么方案来实现。常用的一般是后四种。

常用的分布式事务解决方案:

  • XA 方案
  • TCC 方案
  • 本地消息表
  • 可靠消息最终一致性方案
  • 最大努力通知方案

二、XA

所谓的 XA 方案,即:两阶段、或三阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok,那么就正式提交事务,在各个数据库上执行操作;如果任何其中一个数据库回答不 ok,那么就回滚事务。
这种方式基本很少用:
一般现在的微服务架构很少有一个服务调多个数据库的情况,比如大型电商的订单服务、库存服务、促销服务,各个服务都有单独的数据库,不允许跨业务连所有的数据库。

三、TCC

TCC全称:Try、Confirm、Cancel。最核心的思想是预留资源、取消资源。
在08年的软件开发2.0技术大会上,支付宝程立在PPT大规模SOA系统中的分布事务处理,提出TCC概念。
在这里插入图片描述
举例:比如北京的小明想去泰国游玩,现通过某app预定机票,假设到泰国的路线只有一条(北京-昆明-曼谷并且北京-昆明和昆明-曼谷是两个不同的航空公司),因此小明选择好出行日期后提交订单,此时app后台需要分别请求航空公司A和航空公司B各锁住一张机票(预留资源,此时这张票已经被锁住其他人无法购买此座位,由于资源是特意给小明留的,所以只要小明付款这张票大概率肯定能买到),小明付款后,app后台再请求A和B告诉用户已经支付完成了出票吧;如果小明取消订单或超时未支付,app后端请求A、B告知其订单取消,刚才锁住的票可以释放供别人购买。
https://github.com/changmingxie/tcc-transaction 案例大家可以参考一下

四、本地消息表

本地消息表核心思想是在本地事务记录一条消息或日志用于查询或对账或手动处理后续事宜,其实并不存在各种分布式事务方案的标准流程图或架构图。
举例: 某某商城,用户参与并完成某个活动成功后可领红包,正常情况下用户领取红包是直接调发红包接口,发红包接口返回成功或失败即完成整个业务流程。可是如果我们调用的发红包接口,但是接口直到超时也没有返回成功或失败,这种情况我们就不知道到底红包是发成功了还是失败了。
解决: 我们可以在本地mysql新建一个发红包事务表,表大概有发红包操作流水号、用户pin、红包金额、任务状态、返回码等字段,在本地用户领奖操作和插入红包事务表操作在一个MySQL事务中,定时任务查事务表中状态为未知的记录根据红包流水号查询该次操作是否成功。红包侧需要提供状态回查接口。
上面描述了一个最简单的业务流程,当然还有其他类型的,比如有的业务使用的是mq完成后续操作,但是本地事务表的核心思想基本就是上面这个意思,大家重点体会思想。
在这里插入图片描述

五、可靠消息最终一致性方案

我们这里以rocketMq的事务消息机制为例进行说明。首先看rocketMQ事务消息使用的一些注意点:

  • 消息事务不支持定时和批量。

  • 为了避免一个消息被多次检查,导致半数队列消息堆积,我们限制单个消息的默认检查次数为15次,但用户可以改变这个限制通过修改broker的配置文件中的 transactionCheckMax参数。如果一个消息检查次数超过transactionCheckMax,默认情况下,broker将会丢弃这个消息并同时打印错误日志。用户可以改变这种行为通过覆盖 AbstractTransactionCheckListener 类。

  • 由broker的配置文件中参数 transactionTimeou t决定的特点时间段之后检查事务消息。当发送事务消息时,通过设置用户配置CHECK_IMMUNITY_TIME_IN_SECONDS,用户也可以改变这个限制。这个参数优先于 transactionMsgTimeout 参数。

  • 一个事务消息可能被检查或消费多次。

  • 提交过的消息重新放到用户目标主题可能会失败。目前,它依赖日志记录。通过RocketMQ自身高可用机制确保高可用。如果你想确保事务消息不丢失并且保证事务完整性,建议使用同步双写机制

  • 事务消息的生产者ID不能与其他类型消息的生产者ID共享。不像其他类型消息,事务消息允许回查。MQ server通过生产者ID查询客户端。

rocketMq事务消息先发送一个prepare消息也叫half消息(意思是先把要发送的消息放到broker,但是该消息的状态是待发送,这种消息不会发送到消费端),mq服务端返回half消息的result是否成功,如果成功了则执行本地事务,本地事务执行成功则调用comit将该half消息状态置为可发送状态。如果half消息发送失败则不执行本地事务;
如果本地事务执行成功,comit发送失败,这种情况mq服务端会定时扫描half消息回调发送端的接口确定本地事务是否执行成功,如果成功则发送mq到消费端,如果不成功则将half消息置为无需发送状态。

在这里插入图片描述

六、最大努力通知方案

我们以支付宝对外提供支付接口功能为例
有人看了上面的几种方案就觉得可靠消息解决分布式事务很好用,数据量大和并发性能都很不错,但是还记得文章最开头我们说的吗–并没有什么方案和技术是最好的,要看自己的业务适合用什么方案来实现
支付宝支付api文档:支付宝开放平台api文档

在这里插入图片描述
上图中1.8异步通知使用的就是最大努力通知类型。当然调接口后异步通知的同时也有同步通知,异步通知即使达到最大次数都失败,支付宝也提供有支付订单回查接口。
在这里插入图片描述
另外常用的mq都支持的ack功能,其实就是最大努力通知类型的一种实现,手动ack时只有当手动通知mqServer该消息消费成功后,后续该消息才不会重试,否则消息会按照配置的时间间隔和次数重复投递知道达到最大次数依然失败的话,该消息就会进入死信队列,可以查看死信队列消息手动处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值