mysql spring分布式事务处理

来源:互联网 发布:兴趣图谱源码 编辑:程序博客网 时间:2024/06/10 00:03
1.XA
XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)
2.JTA
作为Java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持,实际上,JTA是基于XA架构上建模的,在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即JTS)实现。像很多其他的java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要由以下几种:


2.1.J2EE容器所提供的JTA实现(JBoss)

2.2.独立的JTA实现:如JOTM,Atomikos.这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。


虽然在Spring中使用Java Transaction API和XA协议进行分布式事务是常见的,但您还有其他选项。最佳实现取决于您的应用程序使用的资源类型以及您愿意在性能,安全性,可靠性和数据完整性之间进行权衡.


虽然在Spring中使用Java Transaction API和XA协议进行分布式事务是常见的,但您还有其他选项。最佳实现取决于您的应用程序使用的资源类型以及您愿意在性能,安全性,可靠性和数据完整性之间进行权衡。在这个JavaWorld功能中,SpringSource的David Syer引导您在Spring应用程序中分析七种分布式事务模式,其中三种是XA和四种。等级:中级

Spring Framework对Java Transaction API(JTA)的支持使应用程序能够使用分布式事务和XA协议,而无需在Java EE容器中运行。然而,即使有了这种支持,XA也是昂贵的,管理不方便或麻烦。那么可能会令人惊喜的是,一定程度的应用程序可以避免使用XA。

为了帮助您了解分布式事务的各种方法所涉及的考虑事项,我将分析七个事务处理模式,提供代码示例以使其具体化。我将以与安全性或可靠性相反的顺序呈现模式,从最普遍的情况下以数据完整性和原子性保证的最高保证开始。当您向下移动列表时,将会使用更多的注意事项和限制。这些模式也大致与运行时间成本相反(从最昂贵的开始)。这些模式都是架构或技术,而不是业务模式,所以我不关注业务用例,只有最小的代码才能看到每个模式的工作。

请注意,只有前三种模式涉及XA,并且在性能上可能不可用或可接受。我不像其他人一样广泛地讨论XA模式,因为它们在其他地方被覆盖,尽管我提供了第一个模式的简单演示。通过阅读本文,您将了解分布式事务可以做什么和不能做什么,以及如何以及何时避免使用XA - 何时不会。

分布式事务和原子性

一个分布式事务是一个涉及多个事务性资源。事务资源的例子是用于与关系数据库和消息中间件通信的连接器。通常,这样的资源有看起来像一个API begin()rollback()commit()。在Java世界中,事务资源通常显示为由底层平台提供的工厂的产品:对于数据库,它是Connection(由...生产DataSource)或Java Persistence API(JPA)EntityManager; 对于Java消息服务(JMS),它是一个Session

在一个典型的例子中,JMS消息触发数据库更新。一个成功的互动分解成一个时间轴,就像这样:

  1. 启动消息交易
  2. 接收消息
  3. 启动数据库事务
  4. 更新数据库
  5. 提交数据库事务
  6. 提交消息交易

如果在更新时发生数据库错误(如约束违规),则所需的顺序如下所示:

  1. 启动消息交易
  2. 接收消息
  3. 启动数据库事务
  4. 更新数据库,失败!
  5. 回滚数据库事务
  6. 回滚消息交易

在这种情况下,消息将在最后一次回滚后返回到中间件,并在某一点返回以在另一个事务中接收。这通常是一件好事,因为否则你可能没有发生故障的记录。(处理自动重试和处理异常的机制不在本文的范围之内。)

两个时间轴的重要特征是它们是原子的,形成一个完全成功或完全失败的单个逻辑事务。

但是什么保证时间线看起来像这些序列之一?事务资源之间的某些同步必须发生,所以如果一个提交它们都做,反之亦然。否则,整个事务不是原子的。事务是分布式的,因为涉及多个资源,没有同步,它不会是原子的。分布式事务的技术和概念上的困难都与资源的同步(或缺乏)有关。

下面讨论的前三种模式是基于XA协议的。因为这些模式已被广泛覆盖,我不会在这里详细介绍。那些熟悉XA模式的人可能希望跳过共享事务资源模式。

全XA与2PC

如果您需要接近防弹保证,您的应用程序的交易将在中断之后恢复,包括服务器崩溃,那么全XA是您唯一的选择。在这种情况下用于同步事务的共享资源是一个特殊的事务管理器,它使用XA协议来协调关于进程的信息。在Java中,从开发人员的角度来看,协议是通过JTA公开的UserTransaction

作为一个系统界面,XA是大多数开发人员从未看到的启用技术。他们需要知道的是,它在那里,它实现了什么,它的成本以及它们如何使用事务资源的影响。成本来自于交易管理器使用的两阶段提交(2PC)协议,以确保所有资源在结束之前就事务的结果达成一致。

如果应用程序启用了Spring,它将使用Spring JtaTransactionManager和Spring声明式事务管理来隐藏底层同步的详细信息。使用XA和不使用XA之间的开发人员的区别在于配置工厂资源:DataSource实例和应用程序的事务管理器。本文包括示例此应用程序(atomikos-db项目)。该DataSource实例和事务管理器是应用程序的唯一XA-或JTA特定的元素。

要查看样品的工作,请运行下面的单元测试com.springsource.open.db。一个简单的MulipleDataSourceTests类只需将数据插入到两个数据源中,然后使用Spring集成支持功能来回滚事务,如清单1所示:

清单1.事务回滚

@Transactional  @Test  public void testInsertIntoTwoDataSources() throws Exception {    int count = getJdbcTemplate().update(        "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0,        "foo");    assertEquals(1, count);    count = getOtherJdbcTemplate()        .update(            "INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)",            0, "INSERT", "foo", new Date());    assertEquals(1, count);    // Changes will roll back after this method exits  }

然后MulipleDataSourceTests验证两个操作是否都回滚,如清单2所示:

清单2.验证回滚

@AfterTransaction  public void checkPostConditions() {    int count = getJdbcTemplate().queryForInt("select count(*) from T_FOOS");    // This change was rolled back by the test framework    assertEquals(0, count);    count = getOtherJdbcTemplate().queryForInt("select count(*) from T_AUDITS");    // This rolled back as well because of the XA    assertEquals(0, count);  }

为了更好地了解Spring事务管理的工作原理以及如何配置它,请参见“ Spring参考指南”。

XA与1PC优化

该模式是许多事务管理器用于避免2PC的开销(如果事务包含单个资源)的优化。您期望您的应用程序服务器能够了解这一点。

XA和最后资源Gambit

许多XA事务管理器的另一个特点是,当所有资源都具有XA功能时,它们仍然可以提供相同的恢复保证。他们通过排序资源并使用非XA资源作为投票来执行此操作。如果无法提交,那么所有其他资源都可以回滚。它接近百分之百防弹 - 但不完全是这样。并且当它失败时,如果不采取额外的步骤(如在一些顶级实现中所做的那样),则它失败而不留下很多痕迹。

共享事务资源模式

在一些系统中降低复杂性和提高吞吐量的一个很好的模式是通过确保系统中的所有事务资源实际上由相同的资源支持来完全消除对XA的需求。这在所有处理用例中显然是不可能的,但它与XA一样坚固,通常要快得多。共享事务资源模式是防弹的,但特定于某些平台和处理场景。

这种模式的一个简单而熟悉的(很多)示例是在Connection使用对象关系映射(ORM)与使用JDBC的组件之间共享数据库。这就是您使用支持ORM工具(如Hibernate,EclipseLink和Java Persistence API(JPA))的Spring事务管理器。相同的事务可以安全地在ORM和JDBC组件之间使用,通常通过由事务控制的服务级方法执行从上面驱动。

这种模式的另一个有效用途是单个数据库的消息驱动更新的情况(如本文的简介中的简单示例)。消息中间件系统需要将数据存储在某处,通常在关系数据库中。为了实现这种模式,所有需要的是将消息系统指向业务数据所在的同一数据库。此模式依赖于消息中间件供应商暴露其存储策略的细节,以便可以将其配置为指向相同的数据库并挂接到同一事务中。

并不是所有的供应商都使这个很容 几乎任何数据库的替代方案是使用Apache ActiveMQ进行消息传递,并将存储策略插入到消息代理中。一旦你知道这个技巧,这是很容易配置的。这在本文的shared-jms-db示例项目中得到证明。应用程序代码(在这种情况下为单元测试)不需要意识到该模式正在使用,因为它在Spring配置中都以声明方式启用。

在示例中的单元测试SynchronousMessageTriggerAndRollbackTests验证一切正在使用同步消息接收。该testReceiveMessageUpdateDatabase方法接收两条消息,并使用它们在数据库中插入两条记录。当此方法退出时,测试框架回滚事务,因此您可以验证消息和数据库更新是否都回滚,如清单3所示:

清单3.验证消息和数据库更新的回滚

@AfterTransactionpublic void checkPostConditions() {  assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));  List<String> list = getMessages();  assertEquals(2, list.size());}

配置的最重要的功能是ActiveMQ持久性策略,将消息传递系统DataSource与业务数据相同,Spring上的标志JmsTemplate用于接收消息。清单4显示了如何配置ActiveMQ持久性策略:

清单4.配置ActiveMQ持久性

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"  depends-on="brokerService">  <property name="brokerURL" value="vm://localhost?async=false" /></bean><bean id="brokerService" class="org.apache.activemq.broker.BrokerService" init-method="start"  destroy-method="stop">    ...  <property name="persistenceAdapter">    <bean class="org.apache.activemq.store.jdbc.JDBCPersistenceAdapter">      <property name="dataSource">        <bean class="com.springsource.open.jms.JmsTransactionAwareDataSourceProxy">          <property name="targetDataSource" ref="dataSource"/>          <property name="jmsTemplate" ref="jmsTemplate"/>        </bean>      </property>      <property name="createTablesOnStartup" value="true" />    </bean>  </property></bean>

清单5显示了Spring JmsTemplate上用于接收消息的标志:

清单5.设置JmsTemplate事务性使用

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  ...  <!-- This is important... -->  <property name="sessionTransacted" value="true" /></bean>

没有sessionTransacted=true,将永远不会进行JMS会话事务API调用,并且不能回滚消息接收。这里的重要组成部分是具有特殊async=false参数和包装器的嵌入式代理,DataSource它们一起确保ActiveMQ Connection与Spring 使用相同的事务性JDBC 。

共享数据库资源有时可以从现有的独立资源合成,特别是如果它们都在相同的RDBMS平台中。企业级数据库供应商都支持同义词(或等效)的概念,其中一个模式中的表(使用Oracle术语)在另一个模式中被声明为同义词。以这种方式,在平台中物理分配的数据可以Connection在JDBC客户端中从事务处理事务处理。例如,在实际系统(与样本相反)中使用ActiveMQ实现共享资源模式通常涉及为消息传递和业务数据创建同义词。

性能和JDBCPersistenceAdapter

ActiveMQ社区的一些人声称JDBCPersistenceAdapter创建性能问题。然而,许多项目和实时系统使用ActiveMQ与关系数据库。在这些情况下,收到的智慧是使用适配器的日记版本来提高性能。这不符合共享资源模式(因为日志本身是一个新的事务资源)。然而,陪审团可能仍然存在JDBCPersistenceAdapter.。实际上,有理由认为使用共享资源可能会提高性能超过期刊的情况。这是Spring和ActiveMQ工程团队积极研究的一个领域。

在非消息场景(多个数据库)中的另一种共享资源技术是使用Oracle数据库链接功能将RDBMS平台级的两个数据库模式链接在一起(参见参考资料)。这可能需要更改应用程序代码或创建同义词,因为引用链接数据库的表名称别名包括链接的名称。

最佳努力1PC模式

最好的努力1PC模式是相当普遍的,但在某些情况下可能会失败,开发人员必须注意。这是一种非XA模式,涉及许多资源的同步单阶段提交。因为没有使用2PC,所以它永远不会像XA交易一样安全,但是如果参与者意识到妥协,通常就够了。许多高容量,高吞吐量的事务处理系统被设置成这样来提高性能。

基本思想是在事务中尽可能延迟所有资源的提交,从而唯一可能出错的是基础架构故障(而不是业务处理错误)。依靠最佳努力的系统1PC的原因是基础设施故障很少,以至于他们能承受风险以换取更高的吞吐量。如果商业处理服务也被设计为幂等,那么在实践中几乎不会出错。

为了帮助您更好地了解模式并分析故障的后果,我将以消息驱动的数据库更新为例。

这笔交易中的两个资源被计入和计数。消息事务在数据库之前启动,它们以相反的顺序结束(提交或回滚)。所以在成功的情况下的顺序可能与本文开头的相同:

  1. 启动消息交易
  2. 接收消息
  3. 启动数据库事务
  4. 更新数据库
  5. 提交数据库事务
  6. 提交消息交易

实际上,前四个步骤的顺序并不重要,除了必须在更新数据库之前收到消息,并且每个事务必须在使用相应的资源之前开始。所以这个顺序是一样的:

  1. 启动消息交易
  2. 启动数据库事务
  3. 接收消息
  4. 更新数据库
  5. 提交数据库事务
  6. 提交消息交易

关键在于最后两个步骤很重要:他们必须按照这个顺序来到最后。订购重要的原因是技术性的,但订单本身是由业务需求决定的。该命令告诉您,这种情况下的一种事务资源是特殊的; 它包含有关如何执行另一项工作的说明。这是一个业务订购:系统无法自动告知哪种方式(尽管如果消息和数据库是两个资源,那么它通常是这样的)。排序的重要原因与故障案例有关。最常见的故障案例(到目前为止)是业务处理失败(不良数据,编程错误等)。在这种情况下,这两个事务都可以轻松地被绑定以响应异常和回滚。

触发回滚的精确机制是不重要的; 有几个可用。重要的是,提交或回滚按照资源中的业务排序的相反顺序进行。在示例应用程序中,消息传递事务必须最后提交,因为该资源中包含业务流程的指令。这是重要的,因为(罕见)失败的情况下,第一个提交成功,第二个失败。由于设计上所有的业务处理都已经完成,所以这种部分失败的唯一原因将是消息中间件的基础设施问题。

请注意,如果数据库资源的提交失败,则净效果仍然是回滚。所以唯一的非原子故障模式是第一个事务提交并且第二个回滚的模式。更一般地说,如果n事务中有资源,则有n-1这样的故障模式,使一些资源在回滚之后处于不一致(提交)状态。在消息数据库用例中,此失败模式的结果是消息回滚并返回到另一个事务中,即使已经成功处理。所以你可以放心地认为,可能发生的最糟糕的事情是可以传递重复的消息。在更一般的情况下,

有些人承担重复邮件的发生频率不足的风险,他们不会试图预期他们。为了更有信心您的业务数据的正确性和一致性,您需要在业务逻辑中了解它们。如果业务处理意识到重复的消息可能到达,那么所有它必须做的(通常以一些额外的成本,但不如2PC)要检查它是否已经处理过该数据,如果有的话,则不做任何操作。这种专业化有时被称为幂等业务服务模式。

示例代码包括使用此模式同步事务资源的两个示例。我会依次讨论,然后再考察一些其他的选择。

春天和消息驱动的POJO

在示例代码的best-jms-db project,参与者被设置成使用主流的配置选项,使得尽力而为1PC图案之后。这个想法是发送到队列的消息被异步侦听器拾取,并用于将数据插入到数据库中的表中。

TransactionAwareConnectionFactoryProxy-股票组件在Spring设计在这个模式中使用-是关键因素。而不是使用提供的原始供应商ConnectionFactory,配置包装ConnectionFactory在处理事务同步的装饰器中。这jms-context.xml,在清单6中显示:

清单6.配置一个TransactionAwareConnectionFactoryProxy包装供应商提供的JMSConnectionFactory

<bean id="connectionFactory"  class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy">  <property name="targetConnectionFactory">    <bean class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="brokerService">      <property name="brokerURL" value="vm://localhost"/>    </bean>  </property>  <property name="synchedLocalTransactionAllowed" value="true" /></bean>

不需要ConnectionFactory知道哪个事务管理器进行同步,因为在需要时只有一个事务处于活动状态,Spring可以在内部处理。驾驶交易由正常DataSourceTransactionManager配置进行处理data-source-context.xml。需要了解事务管理器的组件是将轮询和接收消息的JMS侦听器容器:

<jms:listener-container transaction-manager="transactionManager" >  <jms:listener destination="async" ref="fooHandler" method="handle"/></jms:listener-container>

fooHandlermethod告诉听者容器当消息的“异步”到达队列调用哪个方法,其组件上。处理程序是这样实现的,接受String传入的消息,并使用它来插入记录:

public void handle(String msg) {  jdbcTemplate.update(      "INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, new Date());  }

为了模拟故障,代码使用一个FailureSimulator方面。它检查消息内容,看看它是否应该失败,并以什么方式。maybeFail()清单7所示的方法在FooHandler处理消息之后调用,但在事务结束之前调用,以便它可以影响事务的结果:

清单7. maybeFail()方法

@AfterReturning("execution(* *..*Handler+.handle(String)) && args(msg)")public void maybeFail(String msg) {  if (msg.contains("fail")) {    if (msg.contains("partial")) {      simulateMessageSystemFailure();    } else {      simulateBusinessProcessingFailure();    }  }    }

simulateBusinessProcessingFailure()方法只是抛出一个DataAccessException数据库访问失败的方式。触发此方法时,您将期望所有数据库和消息事务的完全回滚。在示例项目的AsynchronousMessageTriggerAndRollbackTests单元测试中测试了这种情况。

simulateMessageSystemFailure()方法通过瘫痪基础JMS来模拟消息系统中的故障Session。这里的预期结果是部分提交:数据库工作保持提交,但消息回滚。这在AsynchronousMessageTriggerAndPartialRollbackTests单元测试中进行了测试。

样本包还包括在AsynchronousMessageTriggerSunnyDayTests课堂上成功交付所有交易工作的单元测试。

相同的JMS配置和相同的业务逻辑也可以用于同步设置,其中消息在业务逻辑中的阻塞调用中接收,而不是委派给侦听器容器。这种方法也在best-jms-db示例项目中得到体现。在晴天的情况下和全回滚在测试SynchronousMessageTriggerSunnyDayTestsSynchronousMessageTriggerAndRollbackTests分别。

链接交易经理

在最佳努力1PC模式(best-db-db项目)的另一个示例中,事务管理器的粗略实现只是将其他事务管理器的列表链接在一起以实现事务同步。如果业务处理成功,他们都会承诺,如果不是,他们都会回滚。

实现在ChainedTransactionManager其中接受其他事务管理器作为注入属性的列表,如清单8所示:

清单8. ChainedTransactionManager的配置

<bean id="transactionManager" class="com.springsource.open.db.ChainedTransactionManager">  <property name="transactionManagers">    <list>      <bean        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource" />      </bean>      <bean        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="otherDataSource" />      </bean>    </list>  </property></bean>

此配置的最简单的测试只是在数据库中插入某些东西,回滚并检查两个操作都没有跟踪。这MulipleDataSourceTests与XA示例atomikos-db项目中的单元测试一样实现。如果回滚未同步但提交发生了工作,则测试失败。

请记住,资源的顺序很重要。它们是嵌套的,并且提交或回滚以与它们被登记的顺序相反的顺序发生(这是配置中的顺序)。这使得其中一个资源是特殊的:如果有问题,最外层的资源总是回滚,即使唯一的问题是该资源的故障。此外,testInsertWithCheckForDuplicates()测试方法显示了一种保护系统不受部分故障影响的业务流程。在内部资源(otherDataSource在这种情况下)的业务运营中被实施为防御性检查:

int count = otherJdbcTemplate.update("UPDATE T_AUDITS ... WHERE id=, ...?");if (count == 0) {  count = otherJdbcTemplate.update("INSERT into T_AUDITS ...", ...);}

首先尝试使用where子句进行更新。如果没有发生任何事情,您希望在更新中找到的数据将被插入。在这种情况下,额外的保护措施的成本是一个额外的查询(更新),在晴天的情况下。在更复杂的业务流程中,这种成本将相当低,其中每次交易执行许多查询。

其他选项

ChainedTransactionManager样品中具有简单的优点; 它并不打扰许多可用的扩展和优化。另一种方法是TransactionSychronization在第二个资源加入时,使用Spring中的API为当前事务注册回调。这是best-jms-db样本中的方法,其中的关键特征是TransactionAwareConnectionFactory与a 的组合DataSourceTransactionManager。这种特殊情况可以扩展到一般化,以包括使用非JMS资源TransactionSynchronizationManager。优点在于,原则上只有加入交易的资源才能被征召,而不是链中的所有资源。然而,配置仍然需要了解潜在交易中的哪些参与者对应于哪些资源。

此外,Spring工程团队正在考虑为Spring Core提供“最佳努力1PC事务管理器”功能。您可以投票支持JIRA问题,如果您喜欢该模式,并希望在Spring中看到明确和更透明的支持。

非交易访问模式

非交易访问模式需要一种特殊的业务流程才能有意义。这个想法是,有时您需要访问的资源之一是边缘化的,而且根本不需要在事务中。例如,您可能需要在审计表中插入一行,而不管业务事务是否成功; 它只是记录了尝试做某事的尝试。更常见的是,人们高估了他们需要多少资源来对其中一个资源进行读写更改,而且常常只读访问是很好的。否则可以仔细地控制写入操作,以便如果出现问题,可以考虑或忽略。

在这些情况下,停留在事务之外的资源实际上可能拥有自己的事务,但它不会与其他任何事情同步。如果您使用的春天,主要的交易是由驱动PlatformTransactionManager,边际资源可能是一个数据库,Connection从获得的DataSource不是由事务管理器控制。发生这种情况的是每个对边缘资源的访问都具有默认设置autoCommit=true。读取操作将不会看到在另一个未提交的事务中并发发生的更新(假设合理的默认隔离级别),但是写入操作的效果通常会被其他参与者立即看到。

这种模式需要更仔细的分析,对设计业务流程有更多的信心,但与“最佳努力1PC”并没有什么不同。在任何事情出错时提供补偿交易的通用服务对于大多数项目来说太有意义了。但涉及服务功能的简单用例,只能执行一次写入操作(可能还有许多读取操作)并不罕见。这些是非交易性游戏的理想情况。

翼与祷告:反模式

最后一个模式真的是一个反模式。当开发人员不理解分布式事务或者没有意识到有分布式事务时,往往会发生这种情况。没有显式调用底层资源的事务API,您不能只假设所有资源都将加入事务。如果您正在使用Spring事务管理器JtaTransactionManager,它将附加一个事务资源。该事务管理器将用于使用Spring声明式事务管理功能拦截方法执行@Transactional。没有其他资源可以预期在相同的交易中被登记。通常的结果是,在阳光明媚的日子里,一切都可以正常工作,但是一旦有例外,用户就会发现其中一个资源没有回滚。DataSourceTransactionManager

要使用哪种图案?

我将通过分析引入的模式的优缺点来总结,以帮助您了解如何在它们之间进行决策。第一步是认识到你有一个需要分布式交易的系统。有必要(但不够)的条件是有一个具有多个事务资源的进程。足够的条件是这些资源在一个用例中一起使用,通常由您的架构中的服务级别的调用驱动。

如果您没有认识到分布式交易,您可能已经实施了Wing-and-a-Prayer模式。迟早你会看到应该回滚但不是的数据。可能当你看到效果时,这将是实际失败下游的一个很长的路,很难追溯。永久和祷告也可能被开发人员无意中使用,开发人员相信他们受到XA的保护,但没有配置潜在的资源来参与交易。曾经在另一个组织安装了数据库的项目中,他们已经在安装过程中关闭了XA支持。一切都运行好几个月,然后奇怪的失败开始蔓延到业务流程。诊断问题需要很长时间。

如果您使用混合资源的用例足够简单,您可以负担得起分析,也可能需要重构,那么非投资资源模式可能是一种选择。当其中一个资源被读取时,这样做最好,写入操作可以通过检查重复来保护。即使发生故障,非事务性资源中的数据也必须在业务方面有意义。审计,版本控制和日志记录信息通常适用于此类别。故障将相对较为常见(任何时候,真实交易中的任何事情都会回滚),但您可以确信没有任何副作用。

最佳努力1PC适用于需要更多保护常规故障的系统,但不希望2PC的开销。性能改进可能很重要。与非交易资源相比,设置起来更加棘手,但它不应该需要尽可能多的分析,并且用于更通用的数据类型。完全确定数据一致性要求业务处理对于“外部”资源(除了第一个提交之外)是幂等的。消息驱动的数据库更新是一个完美的例子,在Spring中已经有了很好的支持。更异常的场景需要一些额外的框架代码(最终可能是Spring的一部分)。

共享资源模式是完美的特殊情况下,通常涉及特定类型和平台的两个资源(例如,ActiveMQ的与任何RDBMS或Oracle AQ共同位于与Oracle数据库)。优点是极强的鲁棒性和出色的性能。

示例代码更新

随着新版本的Spring和其他组件的发布,本文提供的示例代码将不可避免地显示其年龄。请参阅Spring社区网站以访问作者的最新代码,以及Spring Framework及相关组件的当前版本。

具有2PC的全XA是通用的,并且将始终给出最高的信心和最大的保护,以防止使用多种多样资源的故障。缺点是它是昂贵的,因为协议规定的额外的I / O(但不要写它,直到你尝试),并需要专用平台。有开放源代码的JTA实现可以提供一种方法来破坏应用程序服务器,但许多开发人员仍然认为它们是第二好的。当然,如果他们可以花更多的时间思考系统中的交易边界,那么更多的人会使用JTA和XA。至少如果他们使用Spring,他们的业务逻辑不需要知道如何处理事务,所以平台选择可以推迟。

David Syer博士是SpringSource的首席顾问,总部位于英国。他是Spring Batch项目的创始人和首席工程师,SpringBatch项目是用于构建和配置离线和批处理应用程序的开源框架。他是企业Java会议和行业评论员的常客。最近的出版物出现在服务器端,InfoQ和SpringSource博客中。

了解更多关于这个话题

  • 下载本文的源代码。另请务必访问Spring社区网站,获取本文最新的示例代码。
  • 了解有关JTA和javax.transactionJava的Java文档的更多信息。XAResource
  • “ 使用Spring的XA事务 ”(Murali Kosaraju,JavaWorld,2007年4月)介绍了如何在Java EE容器之外设置具有JTA的Spring。
  • “ XA暴露,第一部分 ”(Mike Spille,Pyrasun,Spille Blog,2004年4月)是一个非常有趣的资源,用于更详细地学习2PC。
  • 了解更多关于Spring事务管理是如何工作的,以及如何通过阅读一般将其配置Spring参考指南,第9章事务管理。
  • “ J2EE 1.2的事务管理 ”(Sanjay Mahapatra,JavaWorld,2000年7月)定义了事务的ACID属性,包括原子性。
  • 在“ XA或XA ”(Guy's Blog,2006年10月)中,Atomikos CTO Guy Pardon主张使用XA。
  • 查看Atomikos文档以了解这个开源事务管理器。
  • “ 如何在Oracle中创建数据库链接 ”(Elisa Gabbert,SearchOracle.com,2004年1月)介绍了如何创建Oracle数据库链接。
  • 称重提供一个“尽力而为”的1PC事务管理器,为Spring框架提供开箱即用的提案。