乐趣区

关于java:分布式事务二之两阶段提交

后面的文章中,咱们介绍了分布式系统中的 CAP 实践和 BASE 实践,本文会就分布式事务的实现计划之一:两阶段提交 (2PC) 进行介绍。2PC 是一个十分经典的强统一、中心化的原子提交协定。中心化是指协定中有两类节点:一个是中心化协调者节点(coordinator)和 N 个参与者节点(partcipant)。

2PC

一致性概念

一致性,是指对每个节点一个数据的更新,整个集群都晓得更新,并且是统一的,假如一个具备 N 个节点的分布式系统,当其满足以下条件时,咱们说这个零碎满足一致性:

  • 全认同: 所有 N 个节点都认同一个后果;
  • 值非法: 该后果必须由 N 个节点中的过半节点提出;
  • 可完结: 决定过程在肯定工夫内完结,不会无休止地进行上来;

一致性的挑战

消息传递异步无序: 事实网络不是一个牢靠的信道,存在音讯延时、失落,节点间消息传递做不到同步有序:

  • 节点宕机: 节点继续宕机,不会复原;
  • 节点宕机复原: 节点宕机一段时间后复原,在分布式系统中最常见;
  • 网络分化: 网络链路呈现问题,将 N 个节点隔离成多个局部;
  • 拜占庭将军问题: 节点或宕机或逻辑失败,甚至不按套路出牌抛出烦扰决定的信息。

2PC 原理

2PC(tow phase commit)两阶段提交。所谓的两个阶段是指:

  1. 第一阶段:筹备阶段(投票阶段)
  2. 第二阶段:提交阶段(执行阶段)。

咱们将提议的节点称为协调者(coordinator),其余参加决定节点称为参与者(participants, 或 cohorts)。

2PC 第一阶段

2PC 的第一阶段是投票环节,投票由协调者节点发动,能够进一步细分为以下步骤:

  1. 事务询问:协调者向所有的参与者发送事务预处理申请,称之为 Prepare,并开始期待各参与者的响应。
  2. 执行本地事务:各个参与者节点执行本地事务操作, 但在执行实现后并不会真正提交数据库本地事务,而是先向协调者报告说:“我这边能够解决了 / 我这边不能解决”。
  3. 各参与者向协调者反馈事务询问的响应:如果参与者胜利执行了事务操作,那么就反馈给协调者 Yes 响应,示意事务能够执行,如果没有参与者胜利执行事务,那么就反馈给协调者 No 响应,示意事务不能够执行。

第一阶段执行完后,会有两种可能。1、所有都返回 Yes. 2、有一个或者多个返回 No。

2PC 第二阶段:失常提交

如果第一阶段所有的参与者都返回 Yes,那么咱们就能够继续执行 2PC 第二阶段的失常提交步骤:

  1. 协调者节点告诉所有的参与者 Commit 事务申请;
  2. 参与者收到 Commit 申请之后,就会正式执行本地事务 Commit 操作,并在实现提交之后开释整个事务执行期间占用的事务资源。

2PC 第二阶段:异样回滚

如果任何一个参与者向协调者反馈了 No 响应,或者期待超时之后,协调者尚未收到所有参与者的反馈响应,那么咱们就须要执行 2PC 第二阶段的回滚操作:

  1. 协调者节点告诉所有的参与者 Rollback 申请;
  2. 参与者收到 Rollback 申请之后,就会正式执行本地事务 Rollback 操作,并在实现提交之后开释整个事务执行期间占用的事务资源。

2PC 存在的问题

通过下面的演示,很容易想到 2pc 所带来的缺点:

  • 性能问题:无论是在第一阶段的过程中, 还是在第二阶段, 所有的参与者资源和协调者资源都是被锁住的, 只有当所有节点筹备结束,事务 协调者 才会告诉进行全局提交,参与者进行本地事务提交后才会开释资源。这样的过程会比拟漫长,对性能影响比拟大。
  • 单节点故障:因为协调者的重要性,一旦协调者产生故障。参与者会始终阻塞上来。尤其在第二阶段,协调者产生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无奈持续实现事务操作。(尽管协调者挂掉,能够从新选举一个协调者,然而无奈解决因为协调者宕机导致的参与者处于阻塞状态的问题)。

2PC 节点故障的状况

协调者失常, 参与者宕机

因为协调者无奈收集到所有参与者的反馈,会陷入阻塞状况。解决方案: 引入超时机制, 如果协调者在超过指定的工夫还没有收到参与者的反馈, 事务就失败, 向所有节点发送终止事务申请。

协调者宕机, 参与者失常

​无论处于哪个阶段,因为协调者宕机,无奈发送提交申请,所有处于执行了操作然而未提交状态的参与者都会陷入阻塞状况。解决方案: 引入协调者备份, 同时协调者需记录操作日志. 当检测到协调者宕机一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态。

协调者和参与者都宕机

如果产生在第一阶段:因为第一阶段,所有参与者都没有真正执行 commit,所以只需从新在残余的参与者中从新选出一个协调者,新的协调者在从新执行第一阶段和第二阶段就能够了。

产生在第二阶段并且挂了的参与者在挂掉之前没有收到协调者的指令:也就是下面的第 2 步挂了,这是可能协调者还没有发送第 2 步就挂了。这种情景下,新的协调者从新执行第一阶段和第二阶段操作。

产生在第二阶段并且有局部参与者曾经执行完 commit 操作:就好比这里订单服务 A 和领取服务 B 都收到协调者发送的 commit 信息,开始真正执行本地事务 commit, 但突发状况,A commit 胜利,B 挂了。这个时候目前来讲数据是不统一的。尽管这个时候能够再通过伎俩让他和协调者通信,再想方法把数据搞成统一的,然而,这段时间内他的数据状态曾经是不统一的了!2PC 无奈解决这个问题。

mysql 对 XA 事务的反对

MySQL 从 5.0.3 开始反对 XA 分布式事务,且只有 InnoDB 存储引擎反对。MySQL Connector/J 从 5.0.0 版本之后开始间接提供对 XA 的反对。

须要留神的是,在 DTP 模型中,mysql 属于资源管理器(RM)。而一个残缺的分布式事务中,个别会存在多个 RM,由事务管理器 TM 来对立进行协调。因而,这里所说的 mysql 对 XA 分布式事务的反对,个别指的是单台 mysql 实例如何执行本人的事务分支。

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 事务

JTA 的实现

Java 事务 API(JTA:Java Transaction API)和它的同胞 Java 事务服务(JTS:Java Transaction Service),为 J2EE 平台提供了分布式事务服务(distributed transaction)的能力。某种程度上,能够认为 JTA 标准是 XA 标准的 Java 版,其把 XA 标准中规定的 DTP 模型交互接口形象成 Java 接口中的办法,并规定每个办法要实现什么样的性能。在 DTP 模型中,规定了模型的五个组成元素:应用程序(Application)、资源管理器(Resource Manager)、事务管理器(Transaction Manager)、通信资源管理器(Communication Resource Manager)、通信协议(Communication Protocol)。而在 JTA 标准中,模型中又多了一个元素 Application Server,如下所示:

上面介绍一下在 JTA 标准中,模型中各个组件的作用:

事务管理器(transaction manager):

处于图中最为外围的地位,其余的事务参与者都是与事务管理器进行交互。事务了管理器提供事务申明,事务资源管理,同步,事务上下文流传等性能。JTA 标准定义了事务管理器与其余事务参与者交互的接口,而 JTS 标准定义了事务管理器的实现要求,因而咱们看到事务管理器底层是基于 JTS 的。

应用服务器(application server):

顾名思义,是利用程序运行的容器。JTA 标准规定,事务管理器的性能应该由 application server 提供,如上图中的 EJB Server。一些常见的其余 web 容器,如:jboss、weblogic、websphere 等,都能够作为 application server,这些 web 容器都实现了 JTA 标准。特地须要留神的是,并不是所有的 web 容器都实现了 JTA 标准,如 tomcat 并没有实现 JTA 标准,因而并不能提供事务管理器的性能。

应用程序(application):

简略来说,就是咱们本人编写的利用,部署到了实现了 JTA 标准的 application server 中,之后咱们就能够咱们 JTA 标准中定义的 UserTransaction 类来申明一个分布式事务。通常状况下,application server 为了简化开发者的工作量,并不一定要求开发者应用 UserTransaction 来申明一个事务,开发者能够在须要应用分布式事务的办法上增加一个注解,就像 spring 的申明式事务一样,来申明一个分布式事务。

特地须要留神的是,JTA 标准规定事务管理器的性能由 application server 提供。然而如果咱们的利用不是一个 web 利用,而是一个本地利用,不须要被部署到 application server 中,无奈应用 application server 提供的事务管理器性能。又或者咱们应用的 web 容器并没有事务管理器的性能,如 tomcat。对于这些状况,咱们能够间接应用一些第三方的事务管理器类库,如 JOTM 和 Atomikos。将事务管理器间接整合进利用中,不再依赖于 application server。

资源管理器(resource manager):

实践上任何能够存储数据的软件,都能够认为是资源管理器 RM。最典型的 RM 就是关系型数据库了,如 mysql,另外一种比拟常见的资源管理器是消息中间件,如 ActiveMQ、RabbitMQ 等,这些都是真正的资源管理器。

事实上,将资源管理器 (resource manager) 称为资源适配器 (resource adapter) 仿佛更为适合。因为在 java 程序中,咱们都是通过 client 来于 RM 进行交互的,例如:咱们通过 mysql-connector-java-x.x.x.jar 驱动包,获取 Conn、执行 sql,与 mysql 服务端进行通信;通过 ActiveMQ、RabbitMQ 等的客户端,来发送音讯等。
失常状况下,一个数据库驱动供应商只须要实现 JDBC 标准即可,一个消息中间件供应商只须要实现 JMS 标准即可。而引入了分布式事务的概念后,DB、MQ 等在 DTP 模型中的作用都是 RM,二者是等价的,须要由 TM 对立进行协调。
为此,JTA 标准定义了一个 XAResource 接口,其定义 RM 必须要提供给 TM 调用的一些办法。之后,不论这个 RM 是 DB,还是 MQ,TM 并不关怀,因为其操作的是 XAResource 接口。而其余标准 (如 JDBC、JMS) 的实现者,同时也对此接口进行实现。如 MysqlXAConnection,就实现了 XAResource 接口。

通信资源管理器(Communication Resource Manager):

这个是 DTP 模型中就曾经存在的概念,对于须要跨利用的分布式事务,事务管理器彼此之间须要通信,这是就是通过 CRM 这个组件来实现的。JTA 标准中,规定 CRM 须要实现 JTS 标准定义的接口。

下图更加直观的演示了 JTA 标准中各个模型组件之间是如何交互的:

交互状况如下:

  1. application 运行在 application server 中;
  2. application 须要拜访 3 个资源管理器 (RM) 上资源:1 个 MQ 资源和 2 个 DB 资源;
  3. 因为这些资源服务器是独立部署的,如果须要同时进行更新数据的话并保障一致性的话,则须要应用到分布式事务,须要有一个事务管理器来对立协调;
  4. Application Server 提供了事务管理器的性能;
  5. 作为资源管理器的 DB 和 MQ 的客户端驱动包,都实现了 XAResource 接口,以供事务管理器调用。

参考文章

mysql 对 XA 事务的反对
3.0 JTA 标准

我是御狐神,欢送大家关注我的微信公众号:wzm2zsd

本文最先公布至微信公众号,版权所有,禁止转载!

退出移动版