分布式事务

180 阅读30分钟

什么是分布式事务

本地事务

本地事务,是指传统的单机数据库事务,必须具备ACID原则:

  • 原子性(A)
    所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间 状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一 样。
  • 一致性(C)
    事务的执行必须保证系统的一致性,在事务开始之前和事务结束以后,数据库的完整性没有 被破坏,就拿转账为例,A有500元,B有500元,如果在一个事务里A成功转给B50元,那么 不管发生什么,那么最后A账户和B账户的数据之和必须是1000元。
  • 隔离性(I)
    所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。数据库保证隔离性包括四种不同的隔离级别: ReadUncommitted(读取未提交内容)
    ReadCommitted(读取提交内容)
    Repeatable Read (可重读)
    Serializable (可串行化)
  • 持久性(D)
    所请的持久性,就是说一旦事务提交了,那么事务对数据所做的变更就完全保存在了数据库 中,即使发生停电,系统岩机也是如此。

因为在传统项目中,项目部署基本是单点式:即单个服务器和单个数据库。这种情况下,数 据库本身的事务机制就能保证ACID的原则,这样的事务就是本地事务。

概括来讲,单个服务与单个数据库的架构中,产生的事务都是本地事务。 其中原子性和持久性就要靠undo和redo日志来实现。

undo和redo

在数据库系统中,既有存放数据的文件,也有存放日志的文件。日志在内存中也是有缓存 Log buffer,也有磁盘文件logfile。
MySQL中的日志文件,有这么两种与事务有关:undo日志与redo日志

undo日志

数据库事务具备原子性(Atomicity),如果事务执行失败,需要把数据回滚。
事务同时还具备持久性(Durability),事务对数据所做的变更就完全保存在了数据库,不能 因为故障而丢失。
原子性可以利用undo日志来实现。

Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份 到Undo Log。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统 可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。 数据库写入数据到磁盘之前,会把数据先缓存在内存中,事务提交时才会写入磁盘中。 用UndoLog实现原子性和持久化的事务的简化过程: 假设有A、B两个数据,值分别为1,2。
A. 事务开始.
B. 记录A=1到undo log.
C. 修改A=3.
D. 记录B=2到undo log.
E. 修改B=4.
F. 将undolog写到磁盘.
G. 将数据写到磁盘.
H. 事务提交.

  • 如何保证持久性
    半务提交前,会把修改数据到磁盘前,也就是说只要事务提交了,数据肯定持久化了。
  • 如何保证原子性
    每次对数据库修改,都会把修改前数据记录在undo log,那么需要回滚时,可以读取 undo log,恢复数据。
  • 若系统在G和H之间崩溃
    此时事务并未提交,需要回滚。而undo logi已经被持久化,可以根据undo log来恢复 数据
  • 若系统在G之前崩渍
    此时数据并未持久化到硬盘,依然保持在事务之前的状态

缺陷:每个事务提交前将数据和Undo Log:写入磁盘,这样会导致大量的磁盘1O,因此性能很低。
如果能够将数据缓存一段时间,就能减少0提高性能。但是这样就会丧失事务的持久性。因 此引入了另外一种机制来实现持久化,即Redo Log。

redo日志

和Undo Log相反,Redo Log记录的是新数据的备份。在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化,减少了IO的次数。
先来看下基本原理:

Undo+Redo事务的简化过程

假设有A、B两个数据,值分别为1,2
A. 事务开始
B. 记录A=1到undo log buffer
C. 修改A=3
D. 记录A-3到redo log buffer
E. 记录B=2到undo log buffer
F. 修改B=4
G. 记录B-4到redo log buffer.. H. 将undo log写入磁盘
I. 将redo log写入磁盘
J. 事务提交

安全和性能问题

  • 如何保证原子性
    如果在事务提交前故障,通过undo log日志恢复数据。如果undo logi都还没写入,那么数据就尚未持久化,无需回滚
  • 如何保证持久化
    大家会发现,这里并没有出现数据的持久化。因为数据已经写入redo log,而redo log持久化到了硬盘,因此只要到了步骤1以后,事务是可以提交的。
  • 内存中的数据库数据何时持久化到磁盘
    因为redo log已经持久化,因此数据库数据写入磁盘与否影响不大,不过为了避免出现脏数据(内存中与磁盘不一致),事务提交后也会将内存数据别入磁盘(也可以按照固设定的频率,新内存数据到磁盘中)。
  • redo log何时写入磁盘
    redo log会在事务提交之前,或者redo log bufferi满了的时候写入磁盘

这里存在两个问题:
问题1:之前是写undo和数据库数据到硬盘,现在是写undo和redo到磁盘,以乎没有减少IO次数

  • 数据库数据写入是随机UO,性能很差

  • redo log在初始化时会开辟一段连续的空间,写入是顺序IO,性能很好

  • 实际上undo log并不是直接写入磁盘,而是先写入到redo log buffer中,当redo log持久化时,undo log就同时持久化到硬盘了。

因此事务提交前,只需要对redo log持久化即可。
另外,redo log并不是写入一次就持久化一次,redo log在内存中也有自己的缓冲池:redo log buffer。每次写redo log都是写入到buffer,在提交时一次性持久化到磁盘,减少IO次数。

分布式事务

分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务:

  • 跨数据源的分布式事务
  • 跨服务的分布式事务
  • 综合情况
  1. 跨数据源
    随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。所以我们对数据库进行了水平拆分,将原单库单表拆分成数据库分片,于是就产生了跨数据库事务问题。

  2. 跨服务
    在业务发展初期,单体应用系统架构,能满足基本的业务需求。但是随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,单体应用逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。

  3. 分布式系统的数据一致性问题
    在数据库水平拆分、服务垂直拆分之后,一个业务操作通常要跨多个数据库、服务才能完成。在分布式网络环境下,我们无法保障所有服务、数据库都百分百可用,一定会出现部分服务、数据库执行成功,另一部分执行失败的问题。
    当出现部分业务操作成功、部分业务操作失败时,业务数据就会出现不一致。

CAP定理

1998年,加州大学的计算机科学家Eric Brewer提出,分布式系统有三个指标,

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

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

Partition tolerance

Partition tolerance,中文叫做分区容错。

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

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

Consistency

Consistency中文叫做"一致性”。意思是,写操作之后的读操作,必须返回该值。举例来说,某条记录是v0,用户向G1发起一个写操作,将其改为v1。接下来用户发起一个读操作会得到 v1。

若用户向G1将其改为v1,向v2读,此时也应该得到v1。G1写之后应该通知G2同步修改,否则就不满足一致性。

Availability

vailability中文叫做"可用性,意思是只要收到用户的请求,服务器就必须给出回应。

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

Consistency和Availability的矛盾

一致性和可用性,为什么不可能同时成立?
答案很简单,因为可能通信失败(即出现分区容错)。

如果保证G2的一致性,那么G1必须在写操作时,锁定G2的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2不能读写,没有可用性。

不如果保证G2的可用性,那么势必不能锁定G2,所以一致性不成立。

如果保证G2的可用性,那么势必不能锁定G2,所以一致性不成立。

综上所述,G2无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性:如果追求所有节点的可用性,那就没法做到一致性。

BASE理论

BASE是三个单问的缩写:

  • Basically Available(甚本可用)
  • Soft state(软状态)
  • Eventually consistent(最终一致性)

解决分布式事务,就是根据上述理论来实现。
还以上面的下单减库存和扣款为例:

订单服务、库存服务、用户服务及他们对应的数据库就是分布式应用中的三个部分。

  • CP方式:现在如果要满足事务的强一致性,就必须在订单服务数据库锁定的同时,对库存服务、用户服务数据资源同时锁定,等待三个服务业务全部处理完成,才可以释放资源。此时如果有其他请求想要操作被锁定的资源就会被阻塞,这样就是满足了CP。
    这就是强一致,弱可用
  • AP方式:三个服务的对应数据库各自独立执行自己的业务,执行本地事务,不要求互相锁定资源。但是这个中间状态下,我们去访问数据库,可遇到数据不一致的情况,不过我们需要做一些后补措施,保证在经过一段时间后,数据最终满足一致性。
    这就是高可用,但弱一致(最终一致)。

由上面的两种思想,延伸出了很多的分布式事务解决方案:

  • XA
  • TCC
  • 可靠消息最终一致
  • AT

分阶段提交

XA

两阶段提交协议,模型中包含几个角色:

  • 应用程序(AP):我们的微服务
  • 事务管理器(TM): 全局事务管理者
  • 资源管理器(RM): 一殷是数据库
  • 通信资源管理器(CRM):是TM和RM间的通信中间件

在该模型中,一个分布式事务(全局事务)可以被拆分成许多个本地事务,运行在不同的AP和RM上。每个本地事务的ACID很好实现,但是全局事务必须保证其中包含的每一个本地事务都能同时成功,若有一个本地事务失败,则所有其它事务都必须回滚。但问题是,本地事务处理过程中,并不知道其它事务的运行状态,因此,就需要通过CM来通知各个本地事务,同步事务执行的状态。

因此,各个本地事务的通信必须有统一的标准,否则不同数据库间就无法通信。X就是X/Open DTP中通信中间件与TM间联系的接口规范,定义了用于通知事务开始、提交、终止、回滚等接口,各个数据库厂商都必须实现这些接口。

  • 阶段一:准备阶段,各个本地事务完成本地事务的准备工作。
  • 阶段二:执行阶段,各个本地事务根据上一阶段执行结果,进行提交或回滚。

这个过程中需要一个协调者(coordinator),还有事务的参与者(voter)。

准备阶段:协调组询问各个事务参与者,是否可以执行事务。每个事务参与者执行事务,写入redo和undo日志,然后反馈事务执行结果,但只要有一个参与者返回的是Disagree,则说明执行失败。

提交阶段:协调组发现有一个或多个参与者返回的是Disagree,认为执行失败。于是向各个事务参与者发出abort指令,各个事务参与者回滚事务。

缺点

  • 单节点故障 若协调者和参与者1在commit阶段宕机,此时其他参与者未收到commit消息,并不能判断当前应该提交或是回滚

  • 性能问题 在整个过程中所有参与者都会锁定本地资源,等待其他事务结束,阻塞时间长,执行效率低。

TCC

TCC模式可以解决2PC中的资源锁定和阻寒问题,减少资源锁定时间 基本原理 它本质是一种补偿的思路。事务运行过程包括三个方法。

  • Try: 资源的检测和预留:
  • Confirm: 执行的业务操作提交:要求Try成功Confirm一定要能成功
  • Cancel: 预留资源释放 执行分两个阶段:
  • 准备阶段(try):资源的检和预留
  • 执行阶段(confirm/cancel):根据上一步结果,判断下面的执行方法。如果上一步中所有事务参与者都成功,则这里执行confirm。反之,执行cancel。

与XA的区别:

  • try、confirm、cancel都是独立的事务,不受其它参与者的影响,不会阻塞等待它人
  • try、confirm、cancel由程序员在业务层编写,锁粒度有代码控制

例如,扣库存的场景,XA的过程

  • 一阶段:扣掉库存,等待提交
  • 二阶段:提交事务,修改库存/回滚事务

TCC的过程:

  • 一阶段:检查库存是否足够,并冻结部分库存,提交事务
    • 检查库存
    • 添加冻结库存字段,值为扣掉的是库存
  • 二阶段:
    • 提交:扣除库存,并删除冻结库存,提交事务
    • 补偿(非回滚):删除冻结库存,提交事务

与XA相比,TCC在性能上有所提升,不会因为等待其他事务而阻塞当前事务;而缺点是代码侵入多,需要人为实现提交补偿逻辑,代码复杂,开发成本高。

可靠消息事务

这种实现方式的思路,其实是源于ebay,其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。

一般分为事务的发起者A和事务的其它参与者B

  • 事务发起者A执行本地事务
  • 事务发起者A通过MQ将需要执行的事务信息发送给事务参与者B
  • 事务参与者B接收到消息后执行本地事务

本地消息表

为了避免消息发送失败或丢失,我们可以把消息持久化到数据库中。实现时有简化版本和解耦合版本两种方式。

简化版本:

  • 事务发起者:
    • 开启本地事务
    • 执行事务相关业务
    • 发送消息到MQ
    • 把消息持久化到数据库,标记为已发送
    • 提交本地事务
  • 事务接收者
    • 接收消息
    • 开启本地事务
    • 处理事务相关业务
    • 修改数据库消息状态为已消费
    • 提交本地事务
  • 额外的定时任务
    • 定时扫描表中超时未消费消息,重新发送

优点

  • 与tcc相比,实现方式较为简单,开发成本低。

缺点

  • 数据一致性完全依赖于消息服务,因此消息服务必须是可靠的。
  • 需要处理被动业务方的幂等问题
  • 被动业务失败不会导致主动业务的回滚,而是重试被动的业务
  • 事务业务与消息发送业务耦合、业务数据与消息表要在一起

解耦版本:独立消息服务 引入独立的消息服务来处理消息持久化

MQ消息服务

RocketMQ事务消息

RocketMQ本身自带了事务消息,可以保证消息的可靠性,原理其实就是自带了本地消息表,与我们上面讲的思路类似

RabbitMQ的消息确认

RabbitMQ确保消息不丢失的思路比较奇特,并没有使用传统的本地表,而是利用了消息的确认机制:

  • 生产者确认机制:确保消息从生产者到达MQ不会有问题
    • 消息生产者发送消息到RabbitMQ时,可以设置一个异步的监听器,监听来自MQ的ACK
    • MQ接收到消息后,会返回一个回执给生产者:
      • 消息到达交换机后路由失败,会返回失败ACK
      • 消息路由失败,持久化失败,会返回失败ACK
      • 消息路由成功,持久化成功,会返回成功ACK
    • 生产者提前编写好不同回执的处理方式
      • 失败回执:等待一定时间后重新发送
      • 成功回执:记录日志等行为
  • 消费者确认机制:确保消息能够被消费者正确消费
    • 消费者需要在监听队列的时候指定手动ACK模式
    • RabbitMQ把消息投递给消费者后,会等待消费者ACK,接收到ACK后才副除消息,如果没有接收到ACK消息会一直保留在服务端,如果消费者断开连接或异常后,消息会投递给其它消费者。
    • 消费者处理完消息,提交事务后,手动ACK如果执行过程中抛出异常,则不会ACK,业务处理失败,等待下一条消息

经过上面的两种确认机制,可以确保从消意生产者到消费者的消意安全,再结合生产者和消 费者两瑞的本地事务,即可保证一个分布式事务的最终一致性

什么是Seata

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决万案。

Seata三大角色

在 Seata 的架构中,一共有三个角色:
TC(Transactlon Coordlnator)-事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM(Transaction Manager)-事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM(Resource Manager)-资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

其中,TC 为单独部罢的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

image.png

Seata分布式事务解决方案

Seata提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入。
  • TCC模式:最终一致的分阶段事务模式,有业务侵入。
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • SAGA模式:长事务模式,有业务侵入。

Seata-AT模式

基本概念: AT模式是一种无侵入的分布式事务解决方案,在AT模式下,用户只需要关注自己的“业务SQL”,用户的“业务SQL”作为一阶段,Seata框架会自动的生成事务的二阶段提交和回滚。

整体机制

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:提交异步化,非常快速的完成;回滚通过一阶段的回滚日志进行反向补偿。

一阶段:在一阶段中,Seata会拦截“业务SQL”,首先解析SQL的语义,找到要更新的业务数据,在数据更新前,保存下来“undo”,然后执行“业务SQL”更新数据,更新之后再次保存数据“redo”,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。

二阶段:相对一阶段,二阶段比较简单,负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否则执行全局提交,回滚用到的就是一阶段记录的“undo Log”,通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有的日志。

image.png

Seata-XA模式

XA模式

首先我们需要先了解一下什么是XA?

XA 规范早在上世纪 90 年代初就被提出,用以解决分布式事务处理这个领域的问题。

注意:不存在某一种分布式事务机制可以完美适应所有场景,满足所有需求。

现在,无论 AT 模式、TCC 模式还是 Saga 模式,这些模式的提出,本质上都源自 XA 规范对某些场景需求的无法满足。

什么是XA协议

XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准

XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口。 XA规范 的目的是允许的多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效。

XA 规范 使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。

XA 规范 在上世纪 90 年代初就被提出。目前,几乎所有主流的数据库都对 XA 规范 提供了支持。

DTP模型定义如下角色:

  • AP:即应用程序,可以理解为使用DTP分布式事务的程序
  • RM:资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库的实例(MySql),通过资源管理器对该数据库进行控制,资源管理器控制着分支事务
  • TM:事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理实务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。
  • DTP模式定义TM和RM之间通讯的接口规范叫XA,简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现的2PC又称为XA方案。

image.png

案例解释:

  1. 应用程序(AP)持有订单库和商品库两个数据源。
  2. 应用程序(AP)通过TM通知订单库(RM)和商品库(RM),来创建订单和减库存,RM此时未提交事务,此时商品和订单资源锁定。
  3. TM收到执行回复,只要有一方失败则分别向其他RM发送回滚事务,回滚完毕,资源锁释放。
  4. TM收到执行回复,全部成功,此时向所有的RM发起提交事务,提交完毕,资源锁释放。

image.png

XA协议的痛点

如果一个参与全局事务的资源 “失联” 了(收不到分支事务结束的命令),那么它锁定的数据,将一直被锁定。进而,甚至可能因此产生死锁。

这是 XA 协议的核心痛点,也是 Seata 引入 XA 模式要重点解决的问题。

Seata的事务模式

Seata 定义了全局事务的框架。

全局事务 定义为若干 分支事务 的整体协调:

  1. TM 向 TC 请求发起(Begin)、提交(Commit)、回滚(Rollback)全局事务。
  2. TM 把代表全局事务的 XID 绑定到分支事务上。
  3. RM 向 TC 注册,把分支事务关联到 XID 代表的全局事务中。
  4. RM 把分支事务的执行结果上报给 TC。(可选)
  5. TC 发送分支提交(Branch Commit)或分支回滚(Branch Rollback)命令给 RM。 developer.qcloudimg.com/http-save/y… Seata 的 全局事务 处理过程,分为两个阶段:
  • 执行阶段 :执行分支事务,并保证执行结果满足是 可回滚的(Rollbackable)持久化的(Durable)
  • 完成阶段: 根据 执行阶段 结果形成的决议,应用通过 TM 发出的全局提交或回滚的请求给 TC,TC 命令 RM 驱动 分支事务 进行 Commit 或 Rollback。

Seata 的所谓事务模式是指:运行在 Seata 全局事务框架下的 分支事务 的行为模式。准确地讲,应该叫作 分支事务模式。

不同的 事务模式 区别在于 分支事务 使用不同的方式达到全局事务两个阶段的目标。即,回答以下两个问题:

  • 执行阶段 :如何执行并 保证 执行结果满足是 可回滚的(Rollbackable)持久化的(Durable)

    • 完成阶段: 收到 TC 的命令后,如何做到分支的提交或回滚?

我们以AT模式举例:

image.png

  • 执行阶段:

    • 可回滚:根据 SQL 解析结果,记录回滚日志
    • 持久化:回滚日志和业务 SQL 在同一个本地事务中提交到数据库
  • 完成阶段:

    • 分支提交:异步删除回滚日志记录
    • 分支回滚:依据回滚日志进行反向补偿更新
Seata的XA模式

XA模式:

在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。

image.png

  • 执行阶段:

    • 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
    • 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
  • 完成阶段:

    • 分支提交:执行 XA 分支的 commit
    • 分支回滚:执行 XA 分支的 rollback
为什么要在Seata中支持XA

为什么要在 Seata 中增加 XA 模式呢?支持 XA 的意义在哪里呢?

本质上,Seata 已经支持的 3 大事务模式:AT、TCC、Saga 都是补偿型 的

补偿型事务处理机制构建在事务资源之上(要么在中间件层面,要么在应用层面),事务资源 本身对分布式事务是无感知的。

事务资源对分布式事务的无感知存在一个根本性的问题:无法做到真正的全局一致性 。

比如,一条库存记录,处在 补偿型 事务处理过程中,由 100 扣减为 50。此时,仓库管理员连接数据库,查询统计库存,就看到当前的 50。之后,事务因为异外回滚,库存会被补偿回滚为 100。显然,仓库管理员查询统计到的 50 就是 脏 数据。所以补偿型事务是存在中间状态的(中途可能读到脏数据)。

XA的价值

与 补偿型 不同,XA 协议 要求 事务资源 本身提供对规范和协议的支持。

因为 事务资源 感知并参与分布式事务处理过程,所以 事务资源(如数据库)可以保障从任意视角对数据的访问有效隔离,满足全局数据一致性。

比如,刚才提到的库存更新场景,XA 事务处理过程中,中间状态数据库存 50 由数据库本身保证,是不会仓库管理员的查询统计看到的。

除了 全局一致性 这个根本性的价值外,支持 XA 还有如下几个方面的好处:

  1. 业务无侵入:和 AT 一样,XA 模式将是业务无侵入的,不给应用设计和开发带来额外负担。
  2. 数据库的支持广泛:XA 协议被主流关系型数据库广泛支持,不需要额外的适配即可使用。
  3. 多语言支持容易:因为不涉及 SQL 解析,XA 模式对 Seata 的 RM 的要求比较少。
  4. 传统基于 XA 应用的迁移:传统的,基于 XA 协议的应用,迁移到 Seata 平台,使用 XA 模式将更平滑。

Seata-TCC事务模式

什么是TCC

TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),他们的具体含义如下:

  • Try:对业务资源的检查并预留。
  • Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功。
  • Cancel:对业务处理进行取消,即回滚操作,该步骤回对 Try 预留的资源进行释放。

TCC 是一种侵入式的分布式事务解决方案,以上三个操作都需要业务系统自行实现,对业务系统有着非常大的入侵性,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。

image.png

Seata的TCC模式

Seata TCC 模式跟通用型 TCC 模式原理一致。

TCC和AT区别

AT 模式基于 支持本地 ACID 事务关系型数据库

  • 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
  • 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

相应的,TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
  • 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
  • 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

image.png

特点

  • 侵入性比较强,并且需要自己实现相关事务控制逻辑。
  • 在整个过程基本没有锁,性能较强。

Seata-Saga事务模式

基本概念

Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务(执行处理时候出错了,给一个修复的机会)都由业务开发实现。

image.png Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

为什么需要Saga

之前我们学习的Seata分布式三种操作模型中所使用的的微服务全部可以根据开发者的需求进行修改,但是在一些特殊环境下,比如老系统,封闭的系统(无法修改,同时没有任何分布式事务引入),那么AT、XA、TCC模型将全部不能使用,为了解决这样的问题,才引用了Saga模型。

比如:事务参与者可能是其他公司的服务或者是遗留系统,无法改造,可以使用Saga模式。

image.png

Saga模式是Seata提供的长事务解决方案,提供了异构系统的事务统一处理模型。在Saga模式中,所有的子业务都不在直接参与整体事务的处理(只负责本地事务的处理),而是全部交由了最终调用端来负责实现,而在进行总业务逻辑处理时,在某一个子业务出现问题时,则自动补偿全面已经成功的其他参与者,这样一阶段的正向服务调用和二阶段的服务补偿处理全部由总业务开发实现。

image.png

Saga状态机

目前Seata提供的Saga模式只能通过状态机引擎来实现,需要开发者手工的进行Saga业务流程绘制,并且将其转换为Json配置文件,而后在程序运行时,将依据子配置文件实现业务处理以及服务补偿处理,而要想进行Saga状态图的绘制,一般需要通过Saga状态机来实现。

基本原理:

  • 通过状态图来定义服务调用的流程并生成json定义文件
  • 状态图中一个节点可以调用一个服务,节点可以配置它的补偿节点
  • 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚
  • 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

image.png

四种模式的对比

image.png

AT模式,是我们常用的处理模式,也是使用最多的一种。

XA和AT,是无侵入式的,二者间的切换只需要更改数据源的代理对象即可。

TCC需要我们手动、自定义,try、Confirm、Cancel。

Saga是针对老项目,不可更改的项目,通过外部的形式解决调用的时候出现的分布式问题,通过Saga中的状态机进行总业务逻辑的设计。