分布式基础-常见分布式事务解决方案

本文主要参考自相关书籍和网络文章,并附上自身的一些理解,如有遗漏或错误,还望海涵并指出。谢谢!

一.分布式事务问题及2PC/3PC协议

1.单机ACID事务

传统的ACID指的是单机情况下(数据库只存在本地库),数据库所具备的原子性、一致性、隔离性与持久性的这四大特性,但是随着项目越来越大,数据量越来越多,单台数据库的磁盘系统被占满了或是单张表数据量太大,导致客户端请求数据库时存在阻塞问题或者是效率问题,此时的做法是将数据库进行分库分表,将数据按一定的规则分配到不同的机器上,此时数据会在多个分布于不同服务器中的数据库,所以产生了分布式事务的问题。

2.分布式事务产生

分布式事务指的是跨系统、跨机器之间的事务,由于其不满足单机的ACID特性,所以相较于普通事务来说复杂了很多。以下是可能产生分布式事务的一些原因:

  1. 垂直切分:将单个表按业务细化出多张表,每张表存放着不同的字段,当需要使用到多张表时将其连接在一起(join)即可获取到所需的数据。

  2. 水平切分:使用多台数据库或表,将原先位于一个库或表的数据按某种规则映射到不同的机器上,以此来减少原先单库或单表中所含的数据大小。

在这里插入图片描述
3. 按业务流程拆分:
通常,出现分库分表的场景还和业务拆分相关,一个很大的系统往往会拆分为多个小系统,每个小系统之间通过RPC来进行调用,相互协调完成业务,这也会出现分布式事务的问题:

在这里插入图片描述

3.两阶段提交协议

两阶段提交协议(2PC)是处理分布式事务的一种基本协议,两阶段指的是prepare和commit/rollback阶段,并且划分出了事务管理器与资源管理器角色:

在两阶段提交协议中,有一个事务管理器和多个资源管理器,事务管理器分两阶段协调资源管理器。

在第一阶段,事务管理器询问所有资源管理器准备是否成功(prepare)。

如果所有资源均准备成功,那么在第二阶段事务管理器会要求所有资源管理器执行提交操作(commit)。

如果任一资源管理器在第一阶段返回准备失败,那么事务管理器会要求所有资源管理器在第二阶段执行回滚操作(rollback)。

通过事务管理器的两阶段协调,最终所有资源管理器要么全部提交,要么全部回滚,最终状态都是一致的。

在这里插入图片描述

4.三阶段提交协议

三阶段提交协议(3PC)相较于2PC来说,新增了一个预提交阶段(preCommit)与超时机制来再次校验当前是否全部资源管理器(参与者)都能够提交成功(或超时),若成功则进行真正的提交阶段,若不成功则回滚。

  • 引入超时ACK
    在这里插入图片描述

  • 三阶段提交成功(commit)
    在这里插入图片描述

  • 三阶段提交失败(rollback)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ahp0WZYC-1586005545020)(http://note.youdao.com/yws/res/18480/886CAAEECB214AE896D91E88DA18F763)]

二.业务侵入的解决方案

1.消息队列

对于分布式事务问题,最简单的一种方式是使用消息队列来作为消息表,在业务的调用流程中通过消息队列来传达事务的执行结果,然后下一个流程作为消费者消费这个执行结果即可。由于要借助消息队列来完成这个过程,所以这种做法是有业务侵入的。

1.1、执行过程

在这里插入图片描述

如上图所示,methodA方法与methodB方法分别部署在不同的服务器众,需要修改不同的数据库,所以存在分布式事务问题,如果methodA执行失败,那么methodB也应该回滚,methodA与methodB需要同时保证成功与回滚。

所以这里可以使用一个RocketMQ来实现事务消息的传达:

1.在执行methodA之前,首先将当前的事务标识封装成消息并传入MQ中,此时使用RocketMQ的话可以把这个消息设置为prepare消息,消费者此时并不会消费掉prepare消息,然后执行methodA方法,此时会出现以下两种异常回滚情况:

1.1、MQ无法接受消息(宕机或阻塞时间过长):
此时整个流程视作失败,不会执行到methodA,并且methodB也不会受影响,可以继续重试发送消息,到一定次数之后抛出异常信息。

1.2、methodA执行失败:此时由于MQ中的消息是prepare消息,并不会被methodB消费,MQ在一定时间后自动把prepare消息给清除掉。

2.当MQ正常接收到prepare消息,并且methodA也执行成功了,此时将MQ中的该prepare消息的状态修改为正式消息,methodB对该消息进行消费,两者同时成功。


1.2、优缺点

使用MQ解决分布式事务问题有如下的优点和缺点:

优点:简单易实现,并且随着MQ集群的出现和RocketMQ的prepare机制,使得这种方案具备高可用性和高性能,能达到最终一致性。是最常见的解决方案。

缺点:消费者只能成功,不能失败,若消费者失败也不能回滚前者的事务;具有一定的业务侵入性。

2.TCC
1.1、执行过程

TCC是一种经典的2PC解决方案,也就是将整个事务控制过程分为Try、Confirm和Cancel阶段,通过Try阶段来对所有的参与者进行资源的检查与预留,如果Try阶段全部成功,那么就对所有参与者进行Confirm;如果Try阶段出现一者失败,那么就对所有参与者进行Cancel。

由于Try、Confirm、Cancel 3个方法均由业务编码实现,所以TCC是具备强业务侵入性的。

在这里插入图片描述
例如,现在有两个系统A和B要做转账业务,要从A系统的数据库中进行扣款,并且对B系统的数据库中进行款项的增加,如何保证分布式事务呢?

在使用TCC之前的做法是直接从A中扣款并向B中加款:

update A set money -= 30 where id = 1; # 对A扣款
update B set money += 30 where id = 1; # 对B加款

如果改为TCC模式的话,那么有:

  • Try 操作:资源的检查和预留。在扣款场景,Try 操作要做的事情就是先检查 A 账户余额是否足够,再冻结要扣款的 30 元(预留资源);此阶段不会发生真正的扣款。
  • Confirm 操作:执行真正业务的提交。在扣款场景下,Confirm 阶段做的事情就是发生真正的扣款,把 A 账户中已经冻结的 30 元钱扣掉。
  • Cancel 操作:预留资源的释放。在扣款场景下,扣款取消,Cancel 操作执行的任务是释放 Try 操作冻结的 30 元钱,使 A 账户回到初始状态。

在这里插入图片描述

1.2、并发控制

在实现TCC时,应当考虑并发性问题,将锁的粒度降到最低,以最大限度提高分布式事务的并发性。以A账户扣款为例,“账户A上有100元,事务T1 要扣除其中的30元,事务T2也要扣除30元,出现并发”。在一阶段Try 操作中,分布式事务T1和分布式事务T2 分别冻结资金的那一部分资金,相互之间无干扰。

这样在分布式事务的二阶段,无论T1是提交还是回滚,都不会对T2 产生影响,这样T1和T2可以在同一笔业务数据上并行执行。

在这里插入图片描述

1.3、幂等保证

由于网络可能出现数据包重传的情况,所以需要考虑Try、Confirm和Cancel这三个阶段的幂等性,也就是Try、Confirm、Cancel 执行一次和执行多次的业务结果是一样的。

在这里插入图片描述

1.4、优缺点

优点:性能提升,具体业务来实现控制资源锁的粒度变小,不会锁定整个资源;数据最终一致性 基于Confirm和Cancel的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。

缺点:TCC会侵入业务过程,并且有可能需要修改数据库表结构来支持。

3.Saga
1.1、执行过程

Saga事务核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。

Saga事务基本协议如下:

每个Saga事务由一系列幂等的有序子事务Ti组成。
每个Ti都有对应的幂等补偿动作Ci,补偿动作用于撤销Ti造成的结果,补偿过程也被称为冲正过程

可以看到,和TCC相比,Saga没有“预留”动作,它的Ti就是直接提交到库。

在这里插入图片描述

例如现在T1、T2、T3为扣减库存、创建订单、支付订单,如果在支付订单时失败了,那么就对原先的操作做回退,也就是补偿冲正:C3支付回滚、C2订单回滚、C1库存回滚,使得数据回到最开始的状态。
在这里插入图片描述

1.2、服务编排

可以将Saga的各事务做成服务编排,制订好每个事务之间的调用顺序和回退顺序,这样可以将整个Saga事务的流程清晰化和规范化:
在这里插入图片描述

1.3、优缺点

优点:实现简单,并且整个Saga事务的流程都十分地清晰,基于消息队列构建的Saga事务还可以避免单点问题。

缺点:具有业务侵入性,需要制订好每个子事务失败后对应的回滚(冲正)操作。

三.业务非侵入的解决方案

看完了业务侵入型的解决方案, 下面来看看两种典型的非业务侵入型解决方案:FMT与XA。

1.FMT
1.1、执行过程

FMT(Framework-managed transaction)即框架管理事务,是一种无侵入的事务解决方案。该模式下,分布式事务框架会托管所有的事务操作,事务的一阶段和二阶段操作均由框架自动生成。

FMT、一阶段:拦截业务SQL语句,保存SQL执行前的快照,执行SQL,并且添加相应的行锁,避免出现并发冲突。

在这里插入图片描述
FMT、二阶段:执行其他业务SQL,若都成功,那么删除一阶段中产生的锁和快照;若其一失败,则重做快照,回滚数据,删除行锁。

在这里插入图片描述

1.2、优缺点

优点:整个事务处理过程由框架自动化完成,无需人工参与,无业务侵入性。

缺点:由于基于框架来对SQL、事务等进行拦截,所以会有性能损耗(譬如需要使用注解、反射、动态代理等机制)。

2.XA
1.1、执行过程

XA模式是另外一种无侵入的分布式事务解决方案,不同于FMT的是,XA模式下,所有一阶段和二阶段都由数据库来完成,而不是由框架来完成。其原理与FMT相似,都是借助了快照来完成回退操作。

在这里插入图片描述

1.2、优缺点

优点:主流的数据库都实现了XA分布式事务方案,所以可以方便地实现。

https://blog.csdn.net/l1028386804/article/details/79769043

缺点:严重依赖于数据库自身的规范和接口,不能高效拓展。

四.总结

在这里插入图片描述

分布式事务的解决基础是2PC和3PC协议。

2PC和3PC协议都划分出两个角色:事务发起者(事务管理器)和跟随者(资源管理器)。

2PC在第一阶段,事务管理器询问所有资源管理器准备是否成功(prepare)。如果所有资源均准备成功,那么在第二阶段事务管理器会要求所有资源管理器执行提交操作(commit)。如果任一资源管理器在第一阶段返回准备失败,那么事务管理器会要求所有资源管理器在第二阶段执行回滚操作(rollback)。通过事务管理器的两阶段协调,最终所有资源管理器要么全部提交,要么全部回滚,最终状态都是一致的。

3PC相对于2PC来说,增加了超时ACK判断以及在真正提交之前增加了预提交阶段(pre commit)来保证事务能够全部提交成功。

分布式事务的常见解决方案分为两大类,一类是业务侵入点、另一类是业务非侵入的。

业务侵入的有MQ、TCC、Saga这三种解决方案。MQ的优点是实现简单并且不会存在单点问题,但是需要使用特定的MQ譬如RocketMQ中的Half Message才能够满足需求,并且消费者对于消息的消费是一定需要成功的;TCC是一种经常使用的方案,它的做法是将整个分布式事务拆分为三个部分:Try、Confirm和Cancel,在Try阶段进行资源的检查与预留,在第二阶段进行事务的统一Confirm和Cancel;Saga的做法是将长事务拆分成顺序执行的子事务,如果其中一个子事务执行失败,则需要继续相应的事务补偿、回滚和数据冲正。

非业务侵入的解决方案有FMT和XA两种选择,FMT的思路是由框架来对SQL进行拦截,并且保存之前的快照,在第二个阶段中如果全部事务都执行成功那么就删除之前的快照并且释放掉行锁,如果执行失败,那么会执行之前的快照。XA和FMT的做法是类似的,不过XA的两个阶段都由数据库自身来保证。

最后,其实每一种方案都有各自的优点与缺点,要结合自身业务的情况和系统的特性来选择具体的方案,选择性地牺牲某些方面以保证某些方面。

五.Thinks

  • https://www.bilibili.com/video/BV1JE411r7p7?from=search&seid=6646201043947197043
  • https://juejin.im/post/5c0e5bf8e51d45063322fe50#heading-20
  • http://springcloud.cn/view/413#Title-H2-46
  • https://juejin.im/post/5cac36326fb9a06882258c41#heading-2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringError

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值