带你读懂事务(下)、分布式事务

之前的文章 带你读懂事务(上)、基本概念

上篇文章我们说了事务的基本概念,以及事务的几种隔离级别,但是说的都是单机事务。随着现在微服务的流行,很多时候我们需要考虑的事务,往往都不再是单节点的事务,这种分布式的事务给我们造成了很大的麻烦,事实上这也是微服务中比较难处理的问题。我们先来看下现在业界分布式事务的解决方案都有哪些:

两阶段提交(2PC)

两阶段提交,就是把原本的事务拆解成了准备阶段和提交阶段这两个阶段(prepare和commit/rollback),在这个阶段中,需要额外的事务管理者TM(Transaction Manager)来对事务的状态进行管理,各个分支事务参与方则称为RM(Resource Manager),这个阶段的流程如下: image.png 我们可以看到,当各个RM的prepare动作成功之后,便向TM发出ok的信息,TM收到RM ok的信息之后,认为事务已经可以提交,就向各个TM发出提交的命令。在这个过程中,如果有任何一个prepare动作失败或者是commit失败,都会导致最终的rollback。

TCC

TCC是一种采用了补偿机制的事务,它把事务分成了以下三个阶段:

  • Try 阶段:主要是对业务系统做检测及资源预留
  • Confirm 阶段:主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
  • Cancel 阶段:主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放

我们举个例子,假如说现在有一个业务流程是电商的付款之后扣库存,则这里涉及到了订单和库存服务,那么如果要使用TCC的话,业务都需要做什么改造

订单服务
try:设置一个中间状态,来表示用户正在支付中
confirm:将状态改为支付成功
cancel:把订单设置为取消

库存服务
try:先用一个冻结库存字段保存冻结库存数,而不是直接扣掉库存
confirm:扣掉库存并清除冻结库存的记录
cancel:把库存加回去

我们可以看出来,TCC和2PC看起来很相似,都是分为了两个阶段去提交,但是实际运用上还是有相当的差别。2PC是XA的标准实现,这种解决方案多数都是需要数据库层面的支持的,因此2PC其实是强一致性事务,因此锁的时间会比较长,高并发之下会有性能问题。TCC的话,由于有中间状态,一致性会差一些,但是性能相对比较友好,只不过对业务的侵入性太大了,需要比较高的改造成本。

本地消息表

这种解决方案其实是基于base理论的,base理论是指Basically Available(基本可用的),Soft state(软状态),Eventual consistency(最终一致性)。它最开始是由eBay提出的,基本思路如下:

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。

消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案改造成本相对较低,并且还能够很容易的做成通用能力,因此也是现在使用的比较多的。

事务消息

事务消息则需要引入mq,它的流程如下:

主流程
 (1) 发送消息(half消息)。
 (2) 服务端响应消息写入结果。
 (3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)。
 (4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)
补偿流程
 (1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”
 (2) Producer收到回查消息,检查回查消息对应的本地事务的状态
 (3) 根据本地事务状态,重新Commit或者Rollback

这种方案比较依赖mq的可靠性,如果要采用这种方案,建议mq选型RocketMq。

FMT(FrameWork Managed Transactions)

这是一个比较特殊的分布式事务解决方案,因为这种方式把分布式事务的处理完全交给了框架,比较典型的是Seata的At模式,这种模式完全不需要修改代码,只需要加上一个注解在事务方法上,就可以完成分布式事务。其原理是业务数据提交时,自动拦截所有的SQL,分别保存SQL对数据修改前后的快照,如果分布式事务成功了,那我们后续只需清理每个数据源中对应的日志数据即可。如果分布式事务需要回滚,就要根据日志数据自动产生用于补偿的逆向SQL。

优点是业务代码改造量是非常小的,但是缺点也很明显,一是性能损耗比较明显,每次修改都需要插入一条log,二是复杂的sql就不能搞得定了。

其他

这里面都是一些用的比较少的,比如3PC,这种方案目前还几乎只存在于理论中,因为不好落地且不实用。

猜你喜欢

转载自juejin.im/post/7126475148252676103