Skip to content

Latest commit

 

History

History
303 lines (163 loc) · 13.7 KB

分布式事务.md

File metadata and controls

303 lines (163 loc) · 13.7 KB

分布式事务


1. 概述

1.1 解决问题

在分布式系统上一次大的复合操作由不同的小的原子操作组成,这些小的操作分布在不同的服务节点上,且属于不同的应用,分布式事务需要保证这些小的操作要么全部成功,要么全部失败。

1.2 使用场景
  1. 跨库操作:⼀个应用中的某个业务需要操作多个库中的多张表;对一张大表进行分库分表拆分,批量操作表中数据的时候可能对多个库进行操作。
  2. 微服务架构:微服务架构中一条调用链路上的有些业务场景(最常见的就是下单操作)需要保证链路上每个节点状态一致(同时提交或回滚)。

2. 分布式事务分类

2.1 刚性事务
  • 满足CAP中的CP理论,通常无业务改造,强⼀致性,原生支持回滚/隔离性,低并发,适合短事务。要使分布式事务达到像本地事务一样,具备强一致性。但由于同步阻塞,处理效率低,不适合大型分布式场景。
2.2 柔性事务
  • 满足BASE理论(基本可用,最终一致),不要求强⼀致性,换句话说,就是AP状态。柔性事务需要业务改造,实现补偿接口,实现资源锁定接口,高并发,适合长事务。
  • 柔性事务主要分为补偿型和通知型

3. 分布式事务解决方案

3.1 XA 和 2PC
  • X/Open DTP模型:

    • DTP模型是⼀个分布式事务模型。这个模型主要使⽤了两段提交(2PC - Two-Phase-Commit)来保证分布式事务的完整性。

    • DTP规范中主要包含了AP、RM、TM三个部分,其中AP是应用程序,是事务发起和结束的地方;RM是资源管理器,主要负责管理每个数据库的连接数据源;TM是事务管理器,负责事务的全局管理,包括事务的生命周期管理和资源的分配协调等。 图

  • XA规范:

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

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

    • XA规范使用2PC阶段提交来保证所有资源同时提交或者回滚事务。

    • 目前所有主流数据库都对XA规范提供了支持。

    Mysql中XA常用命令

    图
  • 2PC:

    XA 事务的基础是两阶段提交(Two-phase commit)协议。需要有一个事务协调者来保证所有的事务参与者都完成了准备工作(第一阶段)。如果协调者收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。

    2PC分为两个阶段处理:

    • 阶段1:准备阶段,执行事务,不提交

    • 阶段2:提交阶段,提交或回滚事务

    2PC适用场景:

    • 单体应用跨库操作场景

    2PC缺点:

    • 性能较低:2PC在执行的过程中所有参与者事务都处于阻塞状态并且不释放数据库连接,不适合高并发场景。

    • 数据不一致:当执⾏事务提交过程中,如果协调者向所有参与者发送Commit请求后,发⽣局部⽹络异常或者协调者在尚未发送完Commit请求,即出现崩溃,最终导致只有部分参与者收到、执⾏请求。于是整个系统将会出现数据不⼀致的情形。

    • 单点问题:⼀旦事务协调者节点挂掉,会导致参与者收不到提交或回滚的通知,从⽽导致参与者节点始终处于事务⽆法完成的中间状态。

3.2 3PC

三阶段提交 (Three-phase commit),是为解决两阶段提交协议的缺点而设计的。与两阶段提交不同的是,三阶段提交是“非阻塞”协议。

与两阶段提交不同的是,三阶段提交有两个改动点。

  1. 引入超时机制。同时在协调者和参与者中都引入超时机制。
  2. 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

3PC的三个阶段处理:

  • 阶段1:canCommit,检查是否能执行,所有参与者反馈yes进入2阶段,否则所有节点abort

  • 阶段2:preCommit,执行事务,不提交,所有参与者反馈yes进入3阶段,否则所有节点abort

  • 阶段3:doCommit,提交获取回滚事务

超时机制:

  • 相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。

3PC缺点:

  1. 阻塞资源:占用数据库连接,性能低

  2. 数据不一致:参与者3阶段如果无法接到协调者通信,会自动提交事务,有的事务成功,有的失败会出现不一致情况。

3.3 TCC

TCC(Try-Confirm-Cancel)分布式事务模型相对于 XA 等传统模型,其特征在于它不依赖资源管理器(RM)对分布式事务的⽀持,⽽是通过对业务逻辑的分解来实现分布式事务

TCC(Try-Confirm-Cancel)是一种常用的分布式事务解决方案,它将一个事务拆分成三个步骤:

  1. T(Try):业务检查阶段,这个阶段主要进行业务校验和检查或者资源的预留;也可以是直接进行业务操作。

  2. C(Confirm):业务确认阶段,这阶段对Try阶段校验过的业务或者预留的资源进行确认。

  3. C(Cancel):业务回滚阶段,这阶段和上面的C(Confirm)是互斥的,用于释放Try阶段预留的资源或者业务。

特点:

  • TCC是业务层⾯的分布式事务,最终⼀致性,不会⼀直持有资源的锁

  • 每个业务都要自己实现Try,Confirm和Cancel接口和逻辑,对业务代码侵入性大

使用场景:

  • 支付,交易相关和钱打交道的场景,严格保证分布式事务要么全部成功,要么全部回滚。
3.4 SAGA

Saga 模型由三部分组成:

  1. LLT(Long Live Transaction):由⼀个个本地事务组成的事务链。

  2. 本地事务:事务链由⼀个个⼦事务(本地事务)组成, LLT = T1+T2+T3+...+Ti。

  3. 补偿:每个本地事务 Ti 有对应的补偿 Ci。

Saga的执⾏顺序有两种:

  1. T1, T2, T3, ..., Tn

  2. T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n

Saga 两种恢复策略:

  1. 向后恢复(Backward Recovery):撤销掉之前所有成功⼦事务。如果任意本地⼦事务失败,则补偿已完成的事务。如异常情况的执⾏顺序T1,T2,T3,..Ti,Ci,...C3,C2,C1。

  2. 向前恢复(Forward Recovery):即重试失败的事务,适⽤于必须要成功的场景,该情况下不需要Ci。执⾏顺序: T1,T2,...,Tj(失败) ,Tj(重试) ,...,Ti。

3.5 本地消息表方案

本地消息表核⼼思想就是将分布式事务拆分成本地事务进⾏处理

发送消息⽅:

  • 需要有⼀个消息表,记录着消息状态相关信息。

  • 业务数据和消息表在同⼀个数据库,要保证它俩在同⼀个本地事务。直接利⽤本地事务,将业务数据和事务消息直接写⼊数据库。

  • 在本地事务中处理完业务数据和写消息表操作后,异步将消息表中信息写到 MQ 消息队列。

消息消费⽅:

  • 处理消息队列中的消息,完成⾃⼰的业务逻辑。

  • 如果本地事务处理成功,则表明已经处理成功了。

  • 如果本地事务处理失败,那么就会重试执⾏。

  • 如果是业务层⾯的失败,给消息⽣产⽅发送⼀个业务补偿消息,通知进⾏回滚等操作。

3.6 可靠消息最终一致性方案

可靠消息最终一致性⽅案主要依靠MQ的半消息机制来实现投递消息和参与者⾃身本地事务的⼀致性保障。

执行流程:

  1. A 系统先向mq发送一条半消息,mq通知发送⽅消息发送成功,如果prepare消息发送失败,则直接取消操作。
  2. 如果本地事务执行成功,则mq发送一条commit消息,如果发送失败,则发送rollback消息。
  3. mq会定期轮询所有prepared消息调用系统A提供的接口查询消息的处理情况,如果该 prepare消息本地事务处理成功,则重新发送commit消息,否则直接rollback该消息。
  4. B系统定期消费mq中的commit消息,执行本地事务,并发送 ack 消息。如果 B 系统中的本地事务失败,会一直不断重试,如果是业务失败,会向A系统发起回滚请求。
3.7 最大努力通知方案

最大努力通知是最简单的一种柔性事务,主要应用于调用外部系统的场景,且被动方处理结果不影响主动方的处理结果。

这个方案的大致意思就是:

  • 系统A本地事务执行完之后,发送个消息到 MQ。
  • 这里会有个专门消费MQ的服务,这个服务会消费 MQ 并调用系统 B 的接口。
  • 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。

4. 分布式事务框架

4.1 seata

Seata 是⼀款开源的分布式事务解决⽅案,致⼒于提供⾼性能和简单易⽤的分布式事务服务。 Seata 将为⽤户提供了 AT、 TCC、 SAGA 和 XA 事务模式,打造⼀站式的分布式解决⽅案。

  • 3大组件

    • TC :事务协调者。负责我们的事务ID的⽣成,事务注册、提交、回滚等。

    • TM:事务发起者。定义事务的边界,负责告知 TC,分布式事务的开始,提交,回滚。

    • RM:资源管理者。管理每个分⽀事务的资源,每⼀个 RM 都会作为⼀个分⽀事务注册在 TC。

  • AT模式

    AT模式也是两阶段的模式,需要再数据库建立回滚日志表,以mysql为例

    CREATE TABLE undo_log (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    branch_id bigint(20) NOT NULL,
    xid varchar(100) NOT NULL,
    context varchar(128) NOT NULL,
    rollback_info longblob NOT NULL,
    log_status int(11) NOT NULL,
    log_created datetime NOT NULL,
    log_modified datetime NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY ux_undo_log (xid,branch_id)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

    第一阶段:

    1. 本地业务数据插入或者更新和undo_log回滚日志同一个本地事务插入库中

    2. 提交前,向 TC 申请全局锁

    3. 提交本地事务

    4. 将本地事务结果上报给TC

    第二阶段-回滚:

    1. 收到 TC 的分支回滚请求,开启一个本地事务

    2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录

    3. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句

    4. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC

    第二阶段-提交:

    1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
    2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。
  • TCC模式

    TCC模式也是两阶段的模式,与AT模式不同,是每个阶段都是执行自定义的逻辑,不依赖于底层数据资源的事务支持。

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

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

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

  • Saga 模式

    Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

    SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:

    1. 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件

    2. 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点

    3. 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚

      注意: 异常发生时是否进行补偿也可由用户自定义决定

    4. 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

    图
  • XA模式

    工作机制:

    1. 整体运行机制

      XA 模式 运行在 Seata 定义的事务框架内:

      • 执行阶段:XA start/XA end/XA prepare + SQL + 注册分支

      • 完成阶段:XA commit/XA rollback

    2. 数据源代理

      获取 XAConnection 两种方式:

      • 方式一:要求开发者配置 XADataSource
      • 方式二:根据开发者的普通 DataSource 来创建
    3. 分支注册

      XA start 需要 Xid 参数。

      这个 Xid 需要和 Seata 全局事务的 XID 和 BranchId 关联起来,以便由 TC 驱动 XA 分支的提交或回滚。

      目前 Seata 的 BranchId 是在分支注册过程,由 TC 统一生成的,所以 XA 模式分支注册的时机需要在 XA start 之前。