分布式事务和数据源配置

【主题】通过修改Jboss数据源配置保证I2SS 中事务操作完整性
【关键词】JTA, UserTransaction, xa-datasource,localtx-datasource
【分类】数据库 / 编码实现
【提供者】赵学慧 系统设计部
【问题描述】
场景描述:
在EA 公共管理系统中进行删除用户操作。使用技术框架I2SS ,应用服务器Jboss4.0.2,数据库 MySql 。
具体问题:
EA 公共管理系统删除用户时涉及的操作非常之多,如果删除失败,发现出现事务不能回滚,虽然在代码中已经进行了事务操作的控制。
【原因分析及解决方法】
通过对Mysql日志进行分析,发现在一次用户删除操作中并不是单纯的使用单个数据库连接来完成。通过进一步的跟踪发现,在I2SS中每一次调用数据库操作的方法总会试图获取一个新的connection。而且还有一个严重的问题就是我们的程序中利用UserTransaction 对事务进行控制(注 此API 仅用于控制JTA事务),而我们的数据源配置确是<local-tx-datasource>。如果利用JTA控制事务,而数据源是本地事务数据源的,两者搭配的情况是事务不会起作用,这就意味着从 JDBC 连接池接收连接时,会从该池将 AutoCommit 设置为 true。这就预示我们的JTA事务控制根本就不起作用。这也就解释了为什么用户删除事务操作失败的原因。
为了支持I2SS框架,目前的解决方式就是替换支持分布式事务的驱动,修改数据源配置将其替换成<xa-datasource>。配置文件如下:
<datasources>
<xa-datasource>
<jndi-name>DataSource</jndi-name>
<track-connection-by-tx>true</track-connection-by-tx>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
<new-connection-sql>set autocommit=1</new-connection-sql>
<no-tx-separate-pools>true</no-tx-separate-pools>
<xa-datasource-property name="Url">jdbc:mysql://127.0.0.1:3306/sms1?autoReconnect=true</xa-datasource-property>
<xa-datasource-property name="User">smsdb</xa-datasource-property>
<xa-datasource-property name="Password">smsdb</xa-datasource-property>

<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>

<exception-sorter-class-name>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<type-mapping>mySQL</type-mapping>

<!--pooling parameters-->
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
<blocking-timeout-millis>5000</blocking-timeout-millis>
<idle-timeout-minutes>15</idle-timeout-minutes>
</xa-datasource>
</datasources>
在上述配置文件中用户色标记出的元素需要特别注意,而且这四个配置元素必须同时使用。下面将进行详细解释:
1、<no-tx-separate-pools>
这个元素的设定使得两个连接池能够分开:一个支持JTA事务的连接,另一个不支持JTA事务的连接。连接池在第一次使用时创建。
2、<track-connection-by-tx>
当这个值设置为true时,连接管理器会保持一个XID-to-connection影射表,当事务结束时而且所有的连接关闭或者未被引用时连接将放回连接池。这样做的副作用是不会挂起或者恢复连接的XAResource里的XID,这个行为和本地事务是一样的。
3、<xa-datasource-class>
指定javax.sql.XASDataSource实现类的全名
4、<new-connection-sql>
当新的连接创建时执行此语句。
在我们的配置文件中,我们进行了如下配置:
<new-connection-sql> set autocommit=1</new-connection-sql>
这样做的原因是因为我们通过MySQLXADataSource中获取的连接默认被设置成autocommit 为false,如果我们的连接并入到事务中时(即事务启动之后获取的连接),会统一由UserTransaction 进行提交。对于那些没有并入事务中的单连接,就会出现操作一直不能提交的错误,可能出现XAER_OUTSIDE 异常。通过上述的设置,我们可以将单连接操作放入一个隔离的连接池中,并将其设置为自动提交。
【避免此类问题的建议】
产生这个问题的原因可以归结为I2SS框架中事务处理不当,connection 封装不合理。为了适应框架改用分布式事务,理论上讲会造成非常大的性能开销。

使用JBoss的带XA的配置文件就可以,例如oracle-xa-ds.xml