分布式事务解决方案

前言

当业务量级扩大之后的分库,以及微服务落地之后的业务服务化,都会产生分布式数据不一致的问题。既然本地事务无法满足需求,因此分布式事务就要登上舞台。

​ 我们有必要先来了解下 CAP 原则和 BASE 理论。CAP 原则是 Consistency(一致性)、Availablity(可用性)和 Partition-tolerance(分区容错性)的缩写,它是分布式系统中的平衡理论。

  • 一致性要求所有节点每次读操作都能保证获取到最新数据;
  • 可用性要求无论任何故障产生后都能保证服务仍然可用;
  • 分区容错性要求被分区的节点可以正常对外提供服务。

在这里插入图片描述

事实上,任何系统只可同时满足其中二个,无法三者兼顾,分区容错性是一个最基本的要求。所以系统解决方案。放弃一定可用性保证强一致性。采用最终一致性保证高可用。

业内比较常用的分布式事务解决方案:

  • 强一致性的两阶段提交协议,三阶段提交协议,比如seata.
  • 最终一致性的可靠事件模式、补偿模式,阿里的 TCC 模式。

简介

XA是由X/Open组织提出的分布式事务的规范。 XA规范主要定义了**(全局)事务管理器™和(局 部)资源管理器(RM)**之间的接口。主流的关系型 数据库产品都是实现了XA接口的。

  • XA接口是双向的系统接口,在事务管理器 (TM)以及一个或多个资源管理器(RM)之 间形成通信桥梁。

  • XA之所以需要引入事务管理器是因为,在分布 式系统中,从理论上讲两台机器理论上无法达 到一致的状态,需要引入一个单点进行协调。

  • 由全局事务管理器管理和协调的事务,可以跨 越多个资源(如数据库或JMS队列)和进程。 全局事务管理器一般使用 XA 二阶段提交协议 与数据库进行交互。
    在这里插入图片描述

组成部分

  1. TC (Transaction Coordinator) 事务协调者维护全局和分支事务的状态,驱动全局事务提交或回滚。

  2. RM资源管理器(resource manager):用来管理系统资源,是通向事务资源的途径。数据库就是一种资源管理器。资源管理还应该具有管理事务提交或回滚的能力。

  3. TM事务管理器(transaction manager):事务管理器是分布式事务的核心管理者。事务管理器与每个资源管理器(resource manager)进行通信,协调并完成事务的处理。事务的各个分支由唯一命名进行标识
    Xid 接口 Xid, Xid 接口是 X/Open 事务标识符 XID 结构的 Java 映射。此接口指定三个访问器方法,以检索全局事务格式 ID、全局事务 ID 和分支限定符。Xid 接口供事务管理器和资源管理器使用。此接口对应用程序不可见。

  • 总体流程
    1.TM向TC注册全局事务
    2.调用各资源管理器(即对各数据库数据进行操作), RM向TC注册分支事务, 此时sql会暂存,不会立即提交
    3.TM向TC下达全局事务提交, 此时TC会依次执行sql
    4.如果有分支事务失败了,则会对之前提交的sql进行回滚

二阶段提交

XA需要两阶段提交: prepare 和 commit.

  • 第一阶段为 准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。

  • 第二阶段为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。

分布式事务的两阶段提交是把整个事务提交分为 prepare 和 commit 两个阶段。以电商系统为例,分布式系统中有订单、账户和库存三个服务,如下图:

img

  1. 第一阶段,事务协调者向事务参与者发送 prepare 请求,事务参与者收到请求后,如果可以提交事务,回复 yes,否则回复 no。

  2. 第二阶段,如果所有事务参与者都回复了 yes,事务协调者向所有事务参与者发送 commit 请求,否则发送 rollback 请求。

存在的问题

  • 本地事务在 prepare 阶段锁定资源,如果有其他事务也要修改 xiaoming 这个账户,就必须等待前面的事务完成。这样就造成了系统性能下降。
  • 协调节点单点故障,如果第一个阶段 prepare 成功了,但是第二个阶段协调节点发出 commit 指令之前宕机了,所有服务的数据资源处于锁定状态,事务将无限期地等待。
  • 数据不一致,如果第一阶段 prepare 成功了,但是第二阶段协调节点向某个节点发送 commit 命令时失败,就会导致数据不一致。

三阶段提交

为了解决两阶段提交的问题,三阶段提交做了改进:

  • 在协调节点和事务参与者都引入了超时机制。
  • 第一阶段的 prepare 阶段分成了两步,canCommi 和 preCommit。

如下图:

img

引入 preCommit 阶段后,协调节点会在 commit 之前再次检查各个事务参与者的状态,保证它们的状态是一致的。但是也存在问题,那就是如果第三阶段发出 rollback 请求,有的节点没有收到,那没有收到的节点会在超时之后进行提交,造成数据不一致。

MySQL对XA的支持

MySQL 从5.0.3开始支持XA分布式事务,且只有InnoDB存储引擎支持。MySQL Connector/J 从5.0.0版本之后开始直接提供对XA的支持。
在这里插入图片描述

需要注意的是, 在DTP模型中,mysql属于资源管理器(RM)。而一个完整的分布式事务中,一般会存在多个RM,由事务管理器TM来统一进行协调。因此,这里所说的mysql对XA分布式事务的支持,一般指的是单台mysql实例如何执行自己的事务分支。

XA 事务SQL语法

https://dev.mysql.com/doc/refman/5.7/en/xa-statements.html

XA {START|BEGIN} xid [JOIN|RESUME]   //开启XA事务,如果使用的是XA START而不是XA BEGIN,那么不支持[JOIN|RESUME],xid是一个唯一值,表示事务分支标识符
XA END xid [SUSPEND [FOR MIGRATE]]   //结束一个XA事务,不支持[SUSPEND [FOR MIGRATE]]
XA PREPARE xid 准备提交
XA COMMIT xid [ONE PHASE] //提交,如果使用了ONE PHASE,则表示使用一阶段提交。两阶段提交协议中,如果只有一个RM参与,那么可以优化为一阶段提交
XA ROLLBACK xid  //回滚
XA RECOVER [CONVERT XID]  //列出所有处于PREPARE阶段的XA事务

下面是一个简单的msyql XA事务案例,演示了mysql作为全局事务中的一个事务分支,将一行记录插入到一个表中

-- 开启一个分布式事务
XA START "xatest";

-- 插入记录
INSERT INTO USER(id,NAME,age) VALUES(12,"tianshozuhi",22);

-- 完成分布式事务
XA END "xatest";

-- 分布式事务提交 阶段一 prepare阶段
XA PREPARE "xatest";

-- 分布式事务提交 阶段二 COMMIT阶段
XA COMMIT "xatest";

-- 分布式事务提交 阶段二 rollback阶段
XA ROLLBACK "xatest"

-- 列出所有处于PREPARE阶段的XA事务
XA  RECOVER
XA事务执行流程

XA事务的状态,按照如下步骤进行展开

  1. 使用XA START来启动一个XA事务,并把它置于ACTIVE状态。

  2. 对于一个ACTIVE状态的 XA事务,我们可以执行构成事务的SQL语句,然后发布一个XA END语句。XA END把事务放入IDLE状态。

  3. 对于一个IDLE 状态XA事务,可以执行一个XA PREPARE语句或一个XA COMMIT…ONE PHASE语句:

    • XA PREPARE把事务放入PREPARED状态。在此点上的XA RECOVER语句将在其输出中包括事务的xid值,因为XA RECOVER会列出处于PREPARED状态的所有XA事务。

    • XA COMMIT…ONE PHASE用于预备和提交事务。xid值将不会被XA RECOVER列出,因为事务终止。

  4. 对于一个PREPARED状态的 XA事务,您可以发布一个XA COMMIT语句来提交和终止事务,或者发布XA ROLLBACK来回滚并终止事务。

针对一个给定的客户端连接而言,XA事务和非XA事务(即本地事务)是互斥的。例如,已经执行了”XA START”命令来开启一个XA事务,则本地事务不会被启动,直到XA事务已经被提交或被 回滚为止。相反的,如果已经使用START TRANSACTION启动一个本地事务,则XA语句不能被使用,直到该事务被提交或被 回滚为止。

最后,如果一个XA事务处于ACTIVE状态,是不能直接进行提交的,如果这样做,mysql会抛出异常:

ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed
when global transaction is in the ACTIVE state
XID数据结构

mysql中使用xid来作为一个事务分支的标识符。事实上xid作为事务分支标识符是在XA规范中定义的,

XA规范定义了一个xid有4个部分组成:

  1. gtrid: 全局事务标识符(global transaction identifier),最大不能超过64字节
  2. bqual: 分支限定符(branch qualifier),最大不能超过64字节
  3. data: xid的值,其是 gtrid和bqual拼接后的内容。因为gtrid和bqual最大都是64个字节,因此data的最大长度为128
  4. formatId: 而formatId的作用就是记录gtrid、bqual的格式,。XA规范建议使用OSI CCR风格定义其格式
通过jdbc操作mysql xa事务

MySQL Connector/J 从5.0.0版本之后开始直接提供对XA的支持,也就是提供了java版本XA接口的实现。意味着我们可以直接通过java代码来执行mysql xa事务。

需要注意的是,业务开发人员在编写代

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值