【架构】分布式事务

目录

前言

为什么需要分布式事务

一、数据库分库分表

二、应用SOA化

三、事务的特性

模型-​X/OpenDTP

一、分布式事务概念

二、模型的概念

三、具体应用

互联网解决方案

一、业务接口整合,避免分布式事务

二、最终一致性方案

三、最大努力通知型

小结


前言

小编学习分布式事务的过程中,对于分布式事务的产生背景、分布式事务的应用场景以及互联网中对于分布式系统的解决方案有了一定的了解,下面简单地做一下总结。

为什么需要分布式事务

一、数据库分库分表

1.如果数据库单表一年产生的数据超过1000W,就要考虑分库分表,原来一个数据库变成多个数据库,一个操作既要访问01库,又要访问02库,要保证数据一致性,就要使用分布式事务。

二、应用SOA化

1.业务的服务化,原来单机支撑了整个交易平台,现在对整个网站进行拆解,分离出用户中心、库存中心、订单中心和商品中心,同时不同的服务对应各自的数据库。

2.如果要同时对订单和库存进行操作,这时需要分布式事务,来保证数据的一致性。

三、事务的特性

1.分布式事务本身是事务,那也就具有ACID四个特性。

2.特性的具体表现

A:原子性(Atomicity)

事务中的各个操作单元要么全部做,要么就全部不做。不能事务执行后,处于只做一半的状态。

例如:银行转账,从 A 账户转 100 元至 B 账户,分为两个步骤:

  • 从 A 账户取 100 元
  • 存入 100 元至 B 账户

C:一致性(Consistency)

事务执行后,必须由一个一致状态变为另外一个一致状态。

例如:现有完整性约束 A+B=100,如果一个事务改变了 A,那么必须得改变 B,使得事务结束后依然满足 A+B=100,否则事务失败。

I:隔离性(Isolation)

事务之间不能相互干扰。

例如:现有有个交易是从 A 账户转 100 元至 B 账户,在这个交易事务还未完成的情况下,如果此时 B 查询自己的账户,是看不到新增加的 100 元的。

D:持久性(Durability)

一旦事务完成,对于数据的变更是永久的。

模型-X/OpenDTP

一、分布式事务概念

1. 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

2.一个大的操作有连个或者更小的操作共同完成,这些小的操作有分布在不同的网络主机上,这些操作,要么全部执行成功,要么全部不执行。

二、模型的概念

1.X/Open DTP(X/Open Distributed Transaction Processing Reference Model) 是X/Open 这个组织定义的一套分布式事务的标准,也就是了定义了规范和API接口,由这个厂商进行具体的实现。这个思想在java 平台里面到处都是。

2.三个组件

AP(Application Program):也就是应用程序,可以理解为使用DTP的程序

RM(Resource Manager):资源管理器,这里可以理解为一个DBMS系统,或者消息服务器管理系统,应用程序通过资源管理器对资源进行控制。资源必须实现XA定义的接口

TM(Transaction Manager):事务管理器,负责协调和管理事务,提供给AP应用程序编程接口以及管理资源管理器

三、具体应用

(一)2PC

1.两个阶段: 提交事务请求(投票) ; 执行事务请求(提交或中断)

阶段一:提交事务请求

(1)TM向所有的AP发送事务内容,询问是否可以执行事务的提交操作,并等待各个AP的响应。

(2)执行事务

各个AP节点执行事务操作,将undo和redo信息记录到事务日志中,尽量把提交过程中所消耗时间的操作和准备都提前完成后确保后续事务提交的成功率

(3)各个AP向TM反馈事务询问的响应

各个AP成功执行了事务操作,那么反馈给TM yes的response;如果AP没有成功执行事务,就反馈TM no的response。

阶段二:执行事务提交

(1)成功提交

(2)出现问题 :中断事务提交

2.出现数据一致性问题:

  • 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
  • 同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态
  • 二阶段无法解决的问题:协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交
  • 单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。

(二)3PC

1.三个阶段

  • canCommit
  • preCommit
  • doCommit

阶段1:CanCommit
1、协调者向所有参与者发出包含事务内容的CanCommit请求,询问是否可以提交事务,并等待所有参与者答复。
2、参与者收到CanCommit请求后,如果认为可以执行事务操作,则反馈YES并进入预备状态,否则反馈NO。

阶段2:PreCommit

此阶段分两种情况:
1、所有参与者均反馈YES,即执行事务预提交。
2、任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务。

事务预提交:(所有参与者均反馈YES时)
1、协调者向所有参与者发出PreCommit请求,进入准备阶段。
2、参与者收到PreCommit请求后,执行事务操作,将Undo和Redo信息记入事务日志中(但不提交事务)。
3、各参与者向协调者反馈Ack响应或No响应,并等待最终指令。

中断事务:(任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈时)
1、协调者向所有参与者发出abort请求。
2、无论收到协调者发出的abort请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

阶段3:do Commit

此阶段也存在两种情况:
1、所有参与者均反馈Ack响应,即执行真正的事务提交。
2、任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务。

提交事务:(所有参与者均反馈Ack响应时)
1、如果协调者处于工作状态,则向所有参与者发出do Commit请求。
2、参与者收到do Commit请求后,会正式执行事务提交,并释放整个事务期间占用的资源。
3、各参与者向协调者反馈Ack完成的消息。
4、协调者收到所有参与者反馈的Ack消息后,即完成事务提交。

中断事务:(任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈时)
1、如果协调者处于工作状态,向所有参与者发出abort请求。
2、参与者使用阶段1中的Undo信息执行回滚操作,并释放整个事务期间占用的资源。
3、各参与者向协调者反馈Ack完成的消息。
4、协调者收到所有参与者反馈的Ack消息后,即完成事务中断。

注意:进入阶段三后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的do Commit请求或abort请求。此时,参与者都会在等待超时之后,继续执行事务提交。

2.改进点

  • 增加了超时机制
  • 第二阶段,如果协调者超时没有接受到参与者的反馈,则自动认为失败,发送abort命令
  • 第三阶段,如果参与者超时没有接受到协调者的反馈,则自动认为成功开始提交事务(基于概率)

3.问题点

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

互联网分布式事务的解决方案

一、业务接口整合,避免分布式事务

二、最终一致性方案

eBay的方案其实是一个最终一致性方案,它主要采用消息队列来辅助实现事务控制流程,方案的核心是将需要分布式处理的任务通过消息队列的方式来异步执行,如果事务失败,则可以发起人工重试的纠正流程。人工重试被更多的应用于支付场景,通过对账系统对事后问题进行处理。

1.消息队列

例如:某个用户产生了一笔交易,那么需要在交易表中增加记录,同时需要修改用户表的金额(余额),由于这两个表属于不同的远程服务,所以就会涉及到分布式事务与数据一致性的问题。

使用消息队列解决:

先启动一个事务,更新交易表(transaction)后,并不直接更新user表,而是将要对user表进行的更新插入到消息队列中。

目标系统收到该消息以后,启动本地事务去对用户表的余额做调整。

可能出现以下情况:

  • 数据库操作成功,向MQ中投递消息也成功
  • 操作数据库失败,不会向MQ中投递消息
  • 操作数据库成功,但是向MQ中投递消息时失败,向外抛出异常。数据库操作回滚

如何保证消息不丢失

现在用的比较普遍的MQ都具有持久化消息的功能,如果消费者宕机或者消费失败,都可以执行重试机制

对于如何避免消息的重复消费

(1)保证消费者的幂等性

如果队列中的消息因为网络异常导致发送多次的情况下,仍然需要保证消息被应用多次与应用一次产生的效果是一样的

(2)通过消费日志表来记录消费状态

增加一个message_applied(msg_id)表,用来记录已经被成功应用的消息。在目标系统执行更新操作之前,先检测该消息是否已经被消费过,消费完成后通过本地事务控制来更新这个“消费表状态”,用来避免消息重复消费问题

2.DTS架构

(1) DTS(Distributed Transaction Service)框架是由支付宝在X/OpenDTP模型的基础上改进的一个设计,定义了类似2PC的标准两阶段接口。

(2)DTS最大的特点是放宽了数据库的强一致约束,保证了数据的最终一致性 。

(3)支付系统接收到会员的支付请求后,需要扣减会员账户余额、增加会员积分(暂时假设需要同步实现)增加商户账户余额。会员系统、商户系统、积分系统是独立的三个子系统,无法通过传统的事务方式进行处理。

TRYING阶段:我们需要做的就是会员资金账户的资金预留,即:冻结会员账户的金额(订单金额)

CONFIRMING阶段:我们需要做的就是会员积分账户增加积分余额,商户账户增加账户余额

CANCELING阶段:该阶段需要执行的就是解冻释放我们扣减的会员余额

3.保证最终一致性的模式

(1)查询模式

任何一个服务操作都提供一个查询接口,用来向外部输出操作执行的状态。服务操作的使用方可以通过接口得知服务操作执行的状态,然后根据不同状态做不同的处理操作为了能够实现查询,每个服务操作都需要有唯一的流水号。

(2)补偿模式

有了查询模式,我们就能够得知操作所处的具体状态,如果整个操作处于不正常状态,我们需要修正操作中的出现问题的子操作。也许是要重新执行,或者取消已完成的操作。通过修复使得整个分布式系统达到最终一致。

根据发起形式又分为:

自动恢复:通过对发生失败操作的接口自动重试或者回滚已经完成的操作

通知运营:如果程序无法自动完成恢复,则通过运营人员手动进行补偿

通知技术:通过监控或者告警通知到技术人员,通过技术手段进行修复

三、最大努力通知型

做支付宝交易接口,一般会在支付宝的回调页面和接口里,解密参数,然后调用系统中更新交易状态相关的服务,将订单更新为付款成功。

同时,只有当我们回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会停止回调请求。否则,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。

这就是一个很典型的补偿例子,跟一些MQ重试补偿机制很类似。

小结

这部分的内容结合源码去理解,可能会理解得更加透彻。

感谢您的访问!