关于java:分布式事务浅析

3次阅读

共计 14345 个字符,预计需要花费 36 分钟才能阅读完成。

前言

分布式事务拆开来其实就是分布式、事务两个概念,分布式简略讲就是不同过程间的零碎进行通信;事务广义上咱们常常把它看作是数据库的事务,事务具备 ACID 个性即:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),艰深来说就是同一个事务中对于数据库的更新操作来说要么都胜利,要么都失败;狭义上来说同一个事务中的一批操作(非局限于数据库操作)要么都胜利,要么都失败;综合来说就是事务的参与者别离位于不同的过程节点中。

分布式实践

既然和分布式无关,那咱们很有必要理解一下分布式系统的几个实践 CAP 和 Base 实践;

CAP 实践

一个分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个根本需要,最多只能同时满足其中的两项;

一致性 :所有节点在同一时间具备统一的数据,以下单为例:用户订单生成,库存缩小,用户付钱等操作要么一起胜利要么一起失败;
可用性 :服务始终可用,而且是失常响应工夫;
分区容错性:分布式系统在遇到某节点或网络分区故障的时候,依然可能对外提供满足一致性和可用性的服务。

对于一个分布式系统而言,分区容错性能够说是一个最根本的要求;所以大部分状况其实都是在 CA 两者之间进行衡量;分布式事务同样存在这样的抉择,比方上面要介绍的 X /Open XA 协定,更加偏向于一致性;然而面对互联网的高并发,这种强一致性引发了很大的性能问题,而基于 BASE 实践的柔性事务可能是更好的一个抉择;

BASE 实践

BASE 是 Basically Available(根本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写。很显著 BASE 实践更加偏向满足 CAP 实践中的 AP,既满足可用性,分区容忍性的零碎,通常可能对一致性要求低一些;

BASE 实践和 ACID 能够说是齐全相同的,ACID 保障的是强一致性就义可用性,BASE 实践是用最终一致性代替强一致性保障可用性;在理论的场景中,不同的业务对数据的要求是不一样的,所以大部分状况下 BASE 实践和 ACID 是联合起来应用的;

基于 BASE 实践的柔性事务典型计划:最大致力告诉型,TCC,异步确保型等;

对于 CAP,ACID,BASE 实践能够通过如下图做一个直观的理解:

分布式事务场景

在介绍分布式事务场景之前,咱们首先对本地事务做一个简略的理解,也是咱们平时最罕用的事务,基本上支流的数据库都反对本地事务,也基本上都满足 ACID 这几个个性;

本地事务

一个过程对应一个数据库,事务的所有参与者都在一个过程内,并且对同一台数据库进行操作;

java.sql.Connection 提供了对事务的提交、回滚等操作,大抵的代码如下所示:

Connection conn = DriverManager.getConnection(...) // 获取数据库连贯
// JDBC 中默认是 true,主动提交事务,false 敞开主动提交
conn.setAutoCommit(false);
try {
    // 数据库操作 1
    // 数据库操作 2
    conn.commit(); // 提交事务} catch (Exception e) {conn.rollback();// 事务回滚
} finally {conn.close();// 敞开链接
}

当然 java 自身没有提供对事务的反对,所有反对都是数据库提供的,比方 mysql 相干事务操作:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t_order0 (user_id,order_id) values ('110','1212');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t_order0 (user_id,order_id) values ('111','1213');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.03 sec)

当然如果每块业务处理事务都要这么写一遍切实是麻烦,齐全能够通过 AOP 做切面解决,Spring 就能够很好的帮咱们实现事务的解决,提供了对立的事务管理接口 PlatformTransactionManager,常见的实现类:

DataSourceTransactionManager:用于治理本地事务;

JtaTransactionManager:反对分布式事务实现了 JTA 标准,应用 XA 协定进行两阶段提交;

<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
</bean>

<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
    <tx:attributes>
        ......
    </tx:attributes>
</tx:advice>

<aop:config>
    ......
</aop:config>

既能够通过切面配置哪些类哪些办法须要做事务处理,也能够通过 @Transitional 注解来配置;

分布式事务

随着 SOA 架构、微服务的风行,分布式事务也呈现在很多场景中,常见的包含:单利用多数据源、多利用单数据源、多利用多数据源;

单利用多数据源

这种状况可能是对不同的业务对数据库做了拆分,然而利用还没做拆分,或者说数据量比拟大对数据库做了分库分表操作;

注:这里的数据源不肯定就是数据库,也可能是 MQ 之类的数据源;

多利用单数据源

这种状况很多呈现在应用 Oracle 数据库的零碎中,数据库功能强大,多个利用共用一个数据库,常见于很多金融零碎;

多利用多数据源

这种状况常见于各种 SOA 架构、微服务零碎中,应该说是目前最常见的场景,各个过程间通过 RPC 调用;

以上几种场景都会用到分布式事务,当然下面也提到 CAP 实践,对于分布式事务的解决形式也是会依据你的业务逻辑做相应的衡量;然而不得不提 DTP 模型,能够说是分布式事务处理的规范;

DTP 模型与 XA 标准

DTP 模型以及 XA 标准是一家叫 X /Open 的组织定义的行业标准;

X/Open 国际联盟有限公司是一个欧洲基金会,它的建设是为了向 UNIX 环境提供规范。它次要的指标是促成对 UNIX 语言、接口、网络和利用的开放式系统协定的制订。它还促成在不同的 UNIX 环境之间的应用程序的互操作性,以及反对对电气电子工程师协会(IEEE)对 UNIX 的可移植操作系统接口(POSIX)标准。

相干标准能够参考如下文档:

DTP(Distributed Transaction Processing)模型:Distributed Transaction Processing: Reference Model

DTP(Distributed Transaction Processing)XA 标准:Distributed Transaction Processing: The XA Specification

DTP 模型

X/Open DTP 模型包含五个基本功能组件:

  • 应用程序(AP):全称 Application Program,定义事务边界并指定形成事务的操作;
  • 资源管理器(RMs):全称 Resource Managers,如数据库或文件拜访零碎,提供对资源的拜访;
  • 事务管理器(TM):全称 Transaction Manager,为事务调配标识符,监控其进度,并负责事务实现和协调故障复原;
  • 通信资源管理器(CRM):全称 Communication Resource Managers,用于管制 TM 域内或跨 TM 域的分布式应用程序之间的通信;
  • 通信协议(CP):全称 Communication protocol,提供分布式应用程序应用并由 CRM 反对的底层通信服务。

模型实例

每个实例能够反对一个 AP、一个 TM 和多个 RMs。分布式应用程序由两个或多个实例示意,每个实例中都蕴含一个 CRM;

这种模型能够说是最简略的模型了,对应的其实就是下面的单利用多数据源的状况;而对于分布式应用程序须要多个实例,模型如下:

能够发现每个模型实例多了一个 CRM 模块,次要用于分布式应用程序间的通信;能够用来解决多利用的状况;

事务管理器作用域

TM 域由一个或多个应用同一 TM 的实例组成,此 TM 对于在该 TM 域中运行的所有应用程序都是通用的。公共 TM 应用逻辑共享的数据结构和日志进行全局事务管理;

当两个实例之间产生分布式通信时,它们具备上下级关系,申请另一个实例参加全局事务的实例称为下级,申请的实例称为隶属实例,特地是没有下级的实例称为根;在 X /opendtp 模型中,通过应用树结构来治理跨分布式 ap 操作的全局事务;

XA 标准

下图显示了 DTP 零碎的本地实例,其中 AP 调用 TM 来结构事务。这些框示意 X /Open DTP 模型中的软件组件;箭头批示控制流的方向;

X/Open 标准的主题是上图中的接口(3),即 TMs 和 RMs 交互的 XA 接口;

XA 接口

RM 和 TM 之间的接口定义如下所示:

  1. ax_reg:向 TM 注册 RM;
  2. ax_unreg:用 TM 登记 RM;
  3. xa_close:终止 AP 对 RM 的应用;
  4. xa_commit:通知 RM 提交事务分支;
  5. xa_complete:测试异步 xa_ 操作是否实现;
  6. xa_end:勾销线程与事务分支的关联;
  7. xa_forget:容许 RM 放弃其对启发式实现事务分支的理解;
  8. xa_open:初始化 RM 以供 AP 应用;
  9. xa_prepare:要求 RM 筹备提交事务分支;
  10. xa_recover:获取 RM 已筹备或启发式实现的 XID 列表;
  11. xa_rollback:通知 RM 回滚事务分支;
  12. xa_start:启动或复原事务分支 - 将 XID 与 RM 的线程申请的将来工作相关联

二阶段提交

XA 标准的根底是二阶段提交协定,XA 标准对二阶段提交做了优化;二阶段提交其实就是将提交分成两个阶段,上面大抵看一下二阶段提交的流程:

第一阶段:预提交阶段
1. 事务询问:协调者会问所有的参与者节点,是否能够执行提交操作
2. 执行事务:各个参与者执行事务操作 如:资源上锁,将 Undo 和 Redo 信息记入事务日志中
3. 参与者向协调者反馈事务询问的响应:如果参与者胜利执行了事务操作,反馈给协调者 Yes 响应,否则 No 响应

第二阶段:执行事务提交
如果协调者从所有的参与者取得的反馈都是 Yes 响应,那么就会执行事务提交
1. 发送提交申请:协调者向参与者发送 Commit 申请
2. 事务提交:参与者承受到 Commit 申请后,会正式执行事务提交操作,并在实现提交之后开释事务资源
3. 反馈事务提交后果:参与者在实现事务提交之后,向协调者发送 Ack 音讯
4. 实现事务:协调者承受到所有参与者反馈的 Ack 音讯后,实现事务

如果任何一个参与者向协调者反馈了 No 响应,或者在期待超时之后,协调者尚无接管到所有参与者的反馈信息,那么就会中断事务
1. 发送回滚申请:协调者向参与者发送 Rollback 申请
2. 事务回滚:参与者利用 Undo 信息来执行事务回滚,并开释事务资源
3. 反馈事务回滚后果:参与者在实现事务回滚之后,向协调者发送 Ack 音讯
4. 中断事务:协调者接管到所有参与者反馈的 Ack 音讯之后,中断事务

因为二阶段提交自身存在着阻塞、单点等问题,后续呈现了改良版本三阶段提交,将第一阶段一分为二,此处不在具体介绍;

规范定义好之后,各种 RM 产品就要去实现这些接口,这样就能够反对分布式事务,最典型的 RM 包含数据库,MQ 等;在一个分布式事务中 RM 往往是有多个的,每一个 RM 提供的 XA 反对,能够了解为一个事务分支,对立交给 TM 来治理;

Mysql XA 反对

要想查看以后 Mysql 是否提供了 XA 反对,能够间接应用如下命令:

其中的 XA 用来示意是否反对,InnoDB 引擎是反对的,其余不反对;

XA 事务 SQL 语句

XA {START|BEGIN} xid [JOIN|RESUME]  // 开启 XA 事务

XA END xid [SUSPEND [FOR MIGRATE]]  // 完结 XA 事务

XA PREPARE xid  // 筹备提交

XA COMMIT xid [ONE PHASE]  // 提交事务

XA ROLLBACK xid  // 回滚事务

XA RECOVER  // 列出所有处于 PREPARE 阶段的 XA 事务

更多能够参考:Mysql XA 文档

上面是一个简略的 XA 事务,它将行作为全局事务的一部分插入表中:

mysql> XA START 'xatest';
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO mytable (i) VALUES(10);
Query OK, 1 row affected (0.04 sec)

mysql> XA END 'xatest';
Query OK, 0 rows affected (0.00 sec)

mysql> XA PREPARE 'xatest';
Query OK, 0 rows affected (0.00 sec)

mysql> XA COMMIT 'xatest';
Query OK, 0 rows affected (0.00 sec)

ActiveMQ XA 反对

ActiveMQ 同样提供了 XA 相干命令反对,如下所示:

    public static final byte BEGIN = 0;  // 开启事务
    public static final byte PREPARE = 1;  // 筹备提交
    public static final byte COMMIT_ONE_PHASE = 2;  // 一阶段提交
    public static final byte COMMIT_TWO_PHASE = 3;  // 二阶段提交
    public static final byte ROLLBACK = 4;  // 回滚事务
    public static final byte RECOVER = 5;  // 列出所有处于 PREPARE 阶段的 XA 事务
    public static final byte FORGET = 6;  // 容许 RM 放弃其对启发式实现事务分支的理解
    public static final byte END = 7;  // 完结 XA 事务

ActiveMQ 通过以上字节标识来表白不同的 XA 接口类型;

各类 RM 曾经提供了对 XA 协定的反对,为了让开发人员更好的应用,以 Java 为例,Java 提供了 JTA 标准,各类 RM 同样须要去实现 JTA 的相干标准接口,上面重点看看 JTA 标准;

JTA 标准

JTA 全称:Java Transaction API,Java 事务 API 能够认为是 XA 标准的 Java 版本,为 JEE 平台提供了分布式事务性能,模型图如下所示:

能够发现和 XA 标准比拟多了一个 Application Server,其实就是的 web 容器,常见的比方:tomcat,weblogic,jboss,websphere 等;除了 tomcat 其余几个容器其实都实现了 JTA 标准,能够提供事务管理器的性能;像 tomcat 这种没有提供事务管理的容器能够借助第三方分布式事务管理器比方:Atomikos 等;

注:JTA 标准并没有指定与通信相干的接口,无关 TM 之间互操作性的更多细节,请参阅 JTS 标准;

更多:JTA 文档

接口定义

Java 将所有事务的标准都定义在了 JTA 包中,外面只有接口没有实现,能够发现此 jar 包最新版为 1.1,2008 年之后就没有更新过,想要看具体的源码间接引入即可:

<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
</dependency>

具体源码如下所示,几个外围接口类用红框标出:

一共 8 个接口别离如下:

  1. XAResource:RM 须要实现的接口,定义 RM 提供给 TM 操作的接口;
  2. Xid:事务 ID;
  3. Status:事务状态,一共 10 个状态;
  4. Sychronization:同步接口;
  5. Transaction:事务接口;
  6. TransactionManager:事务管理器,治理事务的全生命周期
  7. TransactionSynchronizationRegistry:事务同步注册;
  8. UserTransaction:给用户应用的事务客户端,外面封装了用户能够应用事务的接口;

以上这些接口其实都不必开发者去实现,个别由 RM 和 TM 的厂商去实现:

XAResource,Xid 接口:由 RM 厂商实现,常见的比方数据库厂商,MQ 厂商等;

TransactionManager,UserTransaction 接口:能够由 web 容器去实现如 jboss,weblogic 等,也能够由第三方去实现如:Atomikos 等;

RM 实现

常见的 RM 包含数据库、MQ;上面以 Mysql 和 AcitveMQ 为例子来看一下是如何应用的;

数据库

在下面的章节中咱们曾经介绍了 Mysql 对 XA 的反对,也就是 Mysq 厂商曾经提供了对相干性能的反对,其实上面要做的就是驱动程序提供对 Mysql XA 性能的保障,同时须要实现 JTA 中 XAResource,Xid 相干接口;

com.mysql.jdbc.jdbc2.optional.MysqlXAConnection   --> XAResource
com.mysql.jdbc.jdbc2.optional.MysqlXid            --> Xid

以上是 mysql 驱动程序中对 JTA 反对的两个外围类,具体应用也比较简单:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.mysql.jdbc.jdbc2.optional.MysqlXAConnection;
import com.mysql.jdbc.jdbc2.optional.MysqlXid;

public class XAMysql {public static void main(String[] args) throws SQLException {
        // 打印 XA 语句用于调试
        boolean logXaCommands = true;
        Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/ds0", "root", "root");
        XAConnection xaConn1 = new MysqlXAConnection((com.mysql.jdbc.ConnectionImpl) conn1, logXaCommands);
        XAResource rm1 = xaConn1.getXAResource();

        Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/ds1", "root", "root");
        XAConnection xaConn2 = new MysqlXAConnection((com.mysql.jdbc.ConnectionImpl) conn2, logXaCommands);
        XAResource rm2 = xaConn2.getXAResource();

        // 全局事务 id
        byte[] gid = "global".getBytes();
        int formatId = 1;
        try {
            // 事务分支 1
            byte[] bqual1 = "b1".getBytes();
            Xid xid1 = new MysqlXid(gid, bqual1, formatId);
            rm1.start(xid1, XAResource.TMNOFLAGS);
            PreparedStatement ps1 = conn1
                    .prepareStatement("insert into t_order0 (user_id,order_id) values ('110','1212')");
            ps1.execute();
            rm1.end(xid1, XAResource.TMSUCCESS);

            // 事务分支 2
            byte[] bqual2 = "b2".getBytes();
            Xid xid2 = new MysqlXid(gid, bqual2, formatId);
            rm2.start(xid2, XAResource.TMNOFLAGS);
            PreparedStatement ps2 = conn2
                    .prepareStatement("insert into t_order0 (user_id,order_id) values ('111','1213')");
            ps2.execute();
            rm2.end(xid2, XAResource.TMSUCCESS);

            // 两阶段提交
            int rm1_prepare = rm1.prepare(xid1);
            int rm2_prepare = rm2.prepare(xid2);
            if (rm1_prepare == XAResource.XA_OK && rm2_prepare == XAResource.XA_OK) {rm1.commit(xid1, false);
                rm2.commit(xid2, false);
            } else {rm1.rollback(xid1);
                rm2.rollback(xid2);
            }
        } catch (XAException e) {e.printStackTrace();
        }
    }
}

以上不仅介绍了 XAResource 是如何去应用的,同时也简略模仿了分布式事务管理的性能,只有在多个数据源都筹备好的状况下能力提交事务,否则回滚事务,这部分其实应该交给更加业余的组件去实现;

MQ

MQ 厂商很多,不肯定每个 MQ 都实现了 JTA 的相干接口,上面要介绍的 AcitveMQ 实现了 JTA 的 RM 接口:

org.apache.activemq.TransactionContext      --> XAResource
org.apache.activemq.command.XATransactionId --> Xid

上面看一个简略的应用实例:

import javax.jms.JMSException;
import javax.jms.XAQueueConnection;
import javax.jms.XAQueueConnectionFactory;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.apache.activemq.ActiveMQXAConnectionFactory;
import org.apache.activemq.ActiveMQXASession;
import org.apache.activemq.TransactionContext;
import org.apache.activemq.command.XATransactionId;

public class MQXATest {public static void main(String[] args) throws XAException, JMSException {XAQueueConnectionFactory factory = new ActiveMQXAConnectionFactory("tcp://localhost:61616");
        XAQueueConnection qConnection = factory.createXAQueueConnection();
        qConnection.start();
        ActiveMQXASession session = (ActiveMQXASession) qConnection.createXAQueueSession();
        // TransactionContext 实现 XAResource
        TransactionContext tc = session.getTransactionContext();

        // XATransactionId 实现 Xid
        Xid xid = new XATransactionId();
        tc.start(xid, XAResource.TMSUCCESS);
        tc.end(xid, XAResource.TMSUCCESS);
        int prepare = tc.prepare(xid);
        if (prepare == XAResource.XA_OK) {tc.commit(xid, false);
        } else {tc.rollback(xid);
        }
    }
}

以上大抵介绍了一下常见的 RM 厂商对 JTA 接口的反对,总体上分为两步:第一步厂商提供对 XA 接口的反对,第二步不便 Java 用户应用提供实现 JTA 接口的客户端程序(比方驱动程序,客户端 jar 包等);

TM 实现

对于事务管理器的实现下面也说到次要包含:web 容器实现、第三方实现;

web 容器

这里以 JBoss 为例做一个简略介绍,JBoss 事务相干次要在 jboss-transaction 和 jboss-transaction-spi 中,外围类次要包含:

org.jboss.tm.TxManager                                    --> TransactionManager
org.jboss.tm.usertx.client.ServerVMClientUserTransaction  --> UserTransaction

相干配置这里就不做过多介绍,上面重点看一下第三方实现形式,以 Atomikos 为例;

Atomikos

Atomikos 是一家公司的名字,提供了基于 JTA 标准的 XA 分布式事务 TM 的实现,蕴含两个产品:

  1. TransactionEssentials:开源的收费产品;
  2. ExtremeTransactions:商业版免费产品;

两个产品的关系如下图所示:

商业版本提供了更多额定的性能:

  • 反对 TCC:柔性事务的反对,Try-Confirm-Cancel;
  • 反对通过 RMI、IIOP、SOAP 这些近程过程调用技术,进行事务流传;这其实就能够用在微服务常见的场景中:多利用多数据源的状况;

Atomikos 同样提供了对 UserTransaction、TransactionManager 接口的实现:

com.atomikos.icatch.jta.UserTransactionImp
com.atomikos.icatch.jta.TransactionManagerImp

上面看一个简略的分布式管理器的应用:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Properties;

import javax.transaction.UserTransaction;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.jdbc.AtomikosDataSourceBean;

public class AtomikosTest {public static void main(String[] args) {AtomikosDataSourceBean ds1 = createAtomikosDataSourceBean("t_order0");
        AtomikosDataSourceBean ds2 = createAtomikosDataSourceBean("t_order1");

        Connection conn1 = null;
        Connection conn2 = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;

        UserTransaction userTransaction = new UserTransactionImp();
        try {
            // 开启事务
            userTransaction.begin();

            // 执行分支 1
            conn1 = ds1.getConnection();
            ps1 = conn1.prepareStatement("insert into t_order0 (user_id,order_id) values ('110','1212')");
            ps1.execute();

            // 执行分支 2
            conn2 = ds2.getConnection();
            ps2 = conn2.prepareStatement("insert into t_order1 (user_id,order_id) values ('111','1213')");
            ps2.execute();

            // 两阶段提交
            userTransaction.commit();} catch (Exception e) {
            // 异样回滚
            userTransaction.rollback();} finally {// 敞开连贯}
    }

    private static AtomikosDataSourceBean createAtomikosDataSourceBean(String dbName) {Properties p = new Properties();
        p.setProperty("url", "jdbc:mysql://localhost:3306/" + dbName);
        p.setProperty("user", "root");
        p.setProperty("password", "root");

        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        ds.setUniqueResourceName(dbName);

        // MySQL 驱动 XAResource 实现类
        ds.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
        ds.setXaProperties(p);
        return ds;
    }
}

这里应用的 RM 是数据库 Mysql,能够发现 UserTransaction 对 MysqlXADataSource 做了治理,进行对立的事务提交,事务回滚;比照之前 XAMysql 实例中手动提交每个 RM,能够说更加不便简洁;

JTS 标准

上文中也提到 JTA 标准并没有指定与通信相干的接口,无关 TM 之间互操作性的更多细节,须要应用 JTS 标准;看看官网的具体定义:

JTS 规定了事务管理器的实现,事务管理器在高层反对 JTA 标准,在底层实现 OMG 对象事务服务(OTS)标准的 Java 映射。JTS 应用 CORBA OTS 接口实现互操作性和可移植性(即,共传输性和可移植性)。这些接口为任何应用 IIOP(internetinterorb 协定)在 JTS 事务管理器之间生成和流传事务上下文的实现定义了一种规范机制。留神,这也容许应用 IIOP 传输机制上的其余 API;例如,容许 IIOP 上的 RMI。

JTS 个人感觉更像是事务管理器实现上的标准,其中重点提到了反对通过 RMI、IIOP、SOAP 这些近程过程调用技术,进行事务流传;

更多:JTS 文档

其余实现

以上重点介绍了 DTP 模型、XA 标准,Java 依据此标准定义的 JTA 标准,不便 Java 开发者应用;以及围绕这一套模型各种厂商或者一些第三方公司对其做的各种反对,实现了一种比拟通用型的分布式事务处理形式;当然下面也提到这种形式其实更偏向于 CAP 中的 CP,是一种强一致性的实现形式,所以在面对高并发的状况下,性能是其一大缺点;越来越多的互联网公司可能更加偏向于最终一致性实现的分布式事务计划,常见的计划有:TCC,事务音讯,本地事务表等等;

TCC

TCC 全称:Try,Confirm,Cancel 分成三个操作:

Try:资源的预留;

Confirm:确认操作,真正的执行,相似提交;

Cancel:撤销操作,相似回滚操作;

其实能够发现和二阶段提交在流程上很像,都是先去试探,而后再提交或回滚;二者的区别:2PC 更多的是面向资源(数据库,MQ 等),而 TCC 能够面向业务层,范畴更广;另外 2PC 会锁定资源,而 TCC 能够不须要,实现最终一致性;当然 TCC 实现起来也更加简单,对业务的侵入也比拟大;

事务音讯

一些 MQ 反对事务音讯如:RocketMQ,发送的音讯和其余本地事件须要同时胜利同时失败,上面看一下它的流程:

  1. 生产者发送 ” 待确认 ” 音讯;
  2. RocketMQ 接管到音讯进行相干保留操作,胜利当前返回状态给生产者;
  3. 生产者接管到的返回如果为 SEND_OK 状态,将执行本地事务操作;
  4. 依据本地事务执行的后果,生产者执行 commit 还是 rollback;
  5. 如果第四步生产者执行操作失败,服务器会在通过固定时间段对状态为 ” 待确认 ” 的音讯发动回查申请;
  6. 生产者接管到回查申请后依据本地事务的后果返回 commit 还是 rollback;
  7. 服务器收到后果后执行相干操作。

能够发现 RocketMQ 实现的也是最终一致性;

本地事务表

本地事务表:利用各零碎的本地事务来实现分布式事务;将大事务分成小事务来解决;

将本地的业务和音讯插入音讯表的操作放到同一个事务中,本地事务中必定是能够保障一起胜利一起失败的;

接下来能够采纳 MQ 由对方订阅音讯并监听,有音讯时主动触发事件;或者定时轮询扫描的形式,去查看音讯表的数据;

生产端最好保障幂等性,避免反复告诉;

一阶段

一阶段:简略了解就是如果由多个数据源操作,那么就一个一个提交,经常出现在单零碎多数据源的状况;这种模式在非凡状况下是可能呈现事务不统一的状况;spring-data-common 中的 ChainedTransactionManager 就提供了相似的性能:链式事务;这种模式如果在一些外部零碎,网络与硬件环境个别比较稳定,硬件产生故障的概率较小,能够尝试;

Seata

阿里提供的一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简略易用的分布式事务服务;

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

这里就不过多介绍了,官网文档介绍的十分全面;

更多:Seata

总结

本文大抵对分布式事务所波及的点都做了一个简略的介绍,能够把它当作一个纲要性的货色去看,外面很多点只有在真正应用的时候能力发现一些其中的问题;分布式事务能够说是所有技术中的一个难点,解决的计划也很多,这个须要依据业务的理论状况,做出最合适的抉择,这个过程其实也是挺难的,你须要理解每种计划它的优缺点,它实用的范畴,没有一个万能的计划。

感激关注

能够关注微信公众号「回滚吧代码」,第一工夫浏览,文章继续更新;专一 Java 源码、架构、算法和面试。

正文完
 0