分布式事务

发布时间 2023-08-02 15:36:51作者: 疯狂小小波

1.1.事务

​ 数据库事务(简称:事务,Transaction)是指数据库执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。

事务拥有以下四个特性,习惯上被称为ACID特性:

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态是指数据库中的数据应满足完整性约束。除此之外,一致性还有另外一层语义,就是事务的中间状态不能被观察到(这层语义也有说应该属于原子性)。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行,如同只有这一个操作在被数据库所执行一样。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。在事务结束时,此操作将不可逆转。

1.2 本地事务

​ 起初,事务仅限于对单一数据库资源的访问控制,架构服务化以后,事务的概念延伸到了服务中。倘若将一个单一的服务操作作为一个事务,那么整个服务操作只能涉及一个单一的数据库资源,这类基于单个服务单一数据库资源访问的事务,被称为本地事务(Local Transaction)。

本地事务示意图

image

1.3 分布式事务

​ 分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上,且属于不同的应用,分布式事务需要保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

​ 从软件架构的演变,应用中常用的分布式形式大体分诶两种形式:

​ 1.单服务多个数据源

​ 2.多服务多个数据源

1.3.1 单服务多个数据源

​ 最早的分布式事务应用架构很简单,不涉及服务间的访问调用,仅仅是服务内操作涉及到对多个数据库资源的访问。

单服务多数据源分布式示意图

image

当一个服务操作访问不同的数据库资源,又希望对它们的访问具有事务特性时,就需要采用分布式事务来协调所有的事务参与者。对于上面介绍的分布式事务应用架构,尽管一个服务操作会访问多个数据库资源,但是毕竟整个事务还是控制在单一服务的内部。

1.3.2 多服务多个数据源

​ 一个服务操作需要调用另外一个服务,这时的事务出现跨服务性。一个服务中的事务在调用另外一个服务的时候,需要以某种机制将事务流转到另外一个服务,从而使被调用的服务访问的资源也自动加入到该事务当中来。

多服务多数据源分布式事务

image

​ 较之基于单一数据库资源访问的本地事务,分布式事务的应用架构更为复杂。在不同的分布式应用架构下,实现一个分布式事务要考虑的问题并不完全一样,比如对多资源的协调、事务的跨服务传播等,实现机制也是复杂多变。

​ 分布式的实现会在一定的理论基础上建立的,下面我们先来讨论分布式的理论基础。

2.分布式事务基础理论

​ 分布式事务是在分布式系统中来实现的,分布式系统之所以叫分布式,是因为提供服务的各个节点分布在不同机器上,相互之间通过网络来进行交互。

​ 既然是服务间是通过网络来进行交互,那么必然存在网络故障断开的风险,因此网络因素也要考虑到分布式事务的考量标准之中。

2.1.CAP理论

2.1.1.CAP概念

CAP定理是在 1998年加州大学的计算机科学家 Eric Brewer (埃里克.布鲁尔)提出,分布式系统有三个指标

  • Consistency 一致性
  • Availability 可用性
  • Partition tolerance 分区容错

分布式 CAP 理论示意图

image

它们的第一个字母分别是 C、A、P。Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。

2.1.2 分区容错 Partition tolerance

​ 大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。

服务间通讯的分区容错示意图

image

上图中,G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息,G2 可能无法收到。系统设计的时候,必须考虑到这种情况。

一般来说,分区容错无法避免,因此分布式项目中可以认为 CAP 的 P 总是成立。剩下的 C 和 A 无法同时做到。

2.1.3 可用性 Availability

Availability 中文叫做"可用性",意思是只要收到用户的请求,服务器就必须给出回应,不管结果正确与否。

用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,否则就不满足可用性。

客户端请求服务示意图

image

上图解释:

​ 步骤1:客户端将 G1 服务中的 v0 修改为 v1

​ 步骤2: G1 向 G2 发送一条消息,将 G2 中的 v0 也修改为 v1 。同时,用户发送请求给 G1 服务获得数据。

​ 步骤3:考虑到满足 CAP 中的可用性,虽然 G2 中的数据还没有修改过来,但依然还会向客户端给予响应。

上图中这里面临一个选择:

​ 1.等数据同步完成之后再提供服务(响应),同步数据时会阻塞请求,这样保证C。

​ 2.随时可提供服务(响应),但数据不一定是一致的,这样保证A。

在互联网项目的高可用来说,会保存服务的高可用,所以会重点考虑 AP。

2.1.4 一致性 Consistency

Consistency 中文叫做"一致性"。意思是,写操作之后的读操作,必须返回该值。

举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为 v1。

image

上图解释:

​ 步骤1:客户端将 G1 服务中的 v0 修改为 v1

​ 步骤2: G1 向 G2 发送一条消息,将 G2 中的 v0 也修改为 v1 。

​ 同时,用户发送请求给 G1 服务获得数据,考虑到满足 CAP 中的数据一致性,

​ 客户端的请求会出现阻塞等待或任何响应。

​ 步骤3: G2 中的将数据 v0 修改为 v1。

​ 步骤4: G2 服务端数据修改完后客户端再次请求可以得到最新的数据。

上图中这里面临一个选择:

​ 1.等数据同步完成之后再提供服务(响应),同步数据时会阻塞请求,这样保证C。

​ 2.随时可提供服务(响应),但数据不一定是一致的,这样保证A。

在公司项目的数据的强一致性来说,会保存服务的一致性,所以会重点考虑 CP。

2.1.5 CAP组合方式

分布式 CAP 理论示意图

image

​ 通过上面的分析,我们得知:C代表一致性,无论什么时候读的时候都能返回最新的数据状态;A代表可用性,每次请求都能够快速返回,请求不会被阻塞;P代表分区容错性,出现网络问题或服务器宕机还能保证部分功能可用,且而鱼和熊掌不能兼得,无法同时满足CAP三项。那么CAP有哪些组合方式呢:

​ CA,若不需要关注P,那么系统不是分布式系统,加强一致性和可用性,关系数据库按照CA进行设计。

​ CP,放弃可用性,追求一致性和分区容错性,比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。

​ AP,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。比如:订单退款,今日退款成功,明日账户到账,只要在预定的用户可以接受的时间内退款事务走完即可。

2.2.BASE理论

​ BASE理论通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。

​ BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展:

  • 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,如订单的"支付中"状态、不同节点数据副本同步延迟等。

    举例:你参加工作后,通常在拿到第一个月工资的时候,都会选择向家里汇款,当你完成转账操作后,结果银行提示“您的转账预计3日内到账”,而该笔交易的状态为”转账中“。

  • 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,早晚会变为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。

    举例:向家里汇款2天后,家里人收到转载的钱。

  • 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。

    举例:在这个期间,两个银行的通信网络出现问题,导致实际的转账操作无法完成,但你依然能查询到你的该笔交易状态为”转账中“。

BASE理论是对CAP理论的延伸,满足BASE理论的事务,我们称之为“柔性事务”。BASE理论强调虽无法做到强一致性(CAP的一致性就是强一致性),但可以采用适当的弱一致性,即最终一致性。

​ 前面已经了解到了分布式事务的基础理论,在理论的基础上,衍生出来不少解决方案,业界常见的解决方案有2PC、TCC、可靠消息最终一致性等。

3.分布式事务解决方案

3.1 2PC事务

​ 举例:张三和李四好久不见,老友约起聚餐,饭店老板要求先买单,才能出票。这时张三和李四分别抱怨近况不如意,囊中羞涩,都不愿意请客,这时只能AA。只有张三和李四都付款,老板才能出票安排就餐。但由于张三和李四都是铁公鸡,形成了尴尬的一幕:

​ 准备阶段:老板要求张三付款,张三付款。老板要求李四付款,李四付款。

​ 提交阶段:老板出票,两人拿票纷纷落座就餐。

​ 例子中形成了一个事务,若张三或李四其中一人拒绝付款,或钱不够,店老板都不会给出票,并且会把已收款退回。

2PC事务结构图

image

可知XA规范中分布式事务有AP,RM,TM组成:

其中应用程序(Application Program ,简称AP):AP定义事务边界(定义事务开始和结束)并访问事务边界内的资源。

资源管理器(Resource Manager,简称RM):Rm管理计算机共享的资源,许多软件都可以去访问这些资源,资源包含比如数据库、文件系统、打印机服务器等。

事务管理器(Transaction Manager ,简称TM):负责管理全局事务,分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚、失败恢复等。

​ 以上就是生活中的2PC,计算机系统中的2PC是怎么定义的呢?

​ 2PC(两阶段提交)顾名思义就是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),部分关系数据库如Oracle、MySQL支持两阶段提交协议。

提交数据示意图

image

失败情况:

回滚数据示意图

  1. 准备阶段(Prepare phase):事务管理器(TM)给每个参与者(RM)发送Prepare消息,每个参与者在本地执行事务,并写本地的redo和undo日志,但不提交,到达一种“万事俱备,只欠东风”的状态。
  2. 提交阶段(commit phase):如果事务管理器(TM)收到了参与者(RM)的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者(RM)根据TM的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源);

二阶段协议优缺点:

优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。

3.2 TCC补偿机制

TCC(Try/Confirm/Cancel)编程模式,核心思想是:针对每个分支事务操作,都要实现并注册一个与其对应的确认(Confirm)和撤销(Cancel)操作。TCC模型是把锁的粒度完全交给业务处理。它分为三个阶段:

TCC(Try/Confirm/Cancel)编程模式其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

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

TCC结构示意图

image

例如: A要向 B 转账,思路大概是:

我们有一个本地方法,里面依次调用 
1、首先在 Try 阶段,要先调用远程接口把 B和 A的钱给冻结起来。 
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。 
3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。 

优点: 相比两阶段提交,可用性比较强。

缺点: 数据的一致性要差一些。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

3.3 可靠消息最终一致性

消息最终一致性应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。我们可以从下面的流程图中看出其中的一些细节:

image

基本思路就是:

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

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

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

优点: 一种非常经典的实现,实现了最终一致性。

缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。