分布式事务处理

分布式事务处理

        在之前的文章"如何合理的使用动态数据源"中,其实也提到了分布式事务相关的场景如:利用多数据源实现读写分离,但直接使用动态数据源频繁其实是很消耗资源的,而且就是当业务service一个方法中的业务涉及到多数据源来回操作的时候会存在没法保证事务的ACID,基于多数据源这个事务问题,找到了一个比较好的解决方案,能进行分布式的处理,还能保住事务的ACID,首先我们先了解一下什么事务?事务的四大特性分别都是什么意思?

事务:是一组SQL组成的"逻辑处理单元"。

原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。

一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

1.     那什么是分布式事务?

其实简单的理解就是为了保证"不同数据库的数据的一致性"。

2.     分布式事务产生的场景"数据库的分库分表"和"SOA服务化"。

3.     那要想做到做的分布式事务,我们应当怎么做了,最好的办法我们先去了解什么是XA协议。

个人对上面文字理解如下(不一定对,只是个人的理解):

正确理解如下:

3.     XA模式的优缺点:

优点:简单,使用分布式成本低。

缺点:性能不理想,XA无法满足高并发的场景,许多Nosql是不支持XA协议的。

4.     其实除了XA这种解决分布式事务的方法外,还有如下的解决方案,其他的解决方案等有时间再整理。

解决方案:XA,可靠性消息模式,TCC,补偿模式

5.     重点来了,就是Atomikos了,Atomikos是一个为Java平台提供的开源事务管理器。

主要功能:全面奔溃/重启恢复,嵌套事务,为XA和非XA提供内置的JDBC适配器,是标准SUM公司的JTA API的实现。

6.     既然都已经介绍了Atomikos强大功能了,那我们一起搭建一个基于SSM框架的测试代码,测试这个Atomikos的功能是不是都是吹的,核心配置如下:

pom.xml 配置如下:

<!-- 第一步:实现了XA协议的开源第三方支持包  -->
<dependency>
	<groupId>com.atomikos</groupId>
	<artifactId>atomikos-util</artifactId>
	<version>3.7.0</version>
</dependency>
<dependency>
	<groupId>com.atomikos</groupId>
	<artifactId>transactions-jta</artifactId>
	<version>3.7.0</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
	<groupId>com.atomikos</groupId>
	<artifactId>transactions-api</artifactId>
	<version>3.7.0</version>
</dependency>
<dependency>
	<groupId>com.atomikos</groupId>
	<artifactId>transactions-jdbc</artifactId>
	<version>3.7.0</version>
</dependency>
<dependency>
	<groupId>org.codehaus.btm</groupId>
	<artifactId>btm</artifactId>
	<version>2.1.4</version>
</dependency>
<!-- 第一步:实现了XA协议的开源第三方支持包  -->

config.properties 配置如下:

# =====================本地环境=====================
his.db.url=jdbc:mysql://localhost:3306/his?useUnicode=true&amp;characterEncoding=UTF-8
his.db.user=root
his.db.password=root

hispay.db.url=jdbc:mysql://localhost:3306/his_pay?useUnicode=true&amp;characterEncoding=UTF-8
hispay.db.user=root
hispay.db.password=root
# =====================本地环境=====================

jta.properties 配置如下:

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name = tm.out
com.atomikos.icatch.log_base_name = tmlog
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level = INFO
# out log
com.atomikos.icatch.output_dir=/hello/atomikos
com.atomikos.icatch.log_base_dir=/hello/atomikos
com.atomikos.icatch.serial_jta_transactions=false

spring-dispatcher.xml 核心配置如下:

<context:property-placeholder location="classpath:config.properties" />
<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
	  destroy-method="close" abstract="true">
	<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
	<property name="poolSize" value="10" />
	<property name="minPoolSize" value="10"/>
	<property name="maxPoolSize" value="30"/>
	<property name="borrowConnectionTimeout" value="60"/>
	<property name="reapTimeout" value="20"/>
	<!-- 最大空闲时间 -->
	<property name="maxIdleTime" value="60"/>
	<property name="maintenanceInterval" value="60"/>
	<property name="loginTimeout" value="60"/>
	<property name="testQuery">
		<value>select 1</value>
	</property>
</bean>

<!-- his数据源  -->
<bean id="hisDataSource" parent="abstractXADataSource">
	<!-- value只要两个数据源不同就行,随便取名 -->
	<property name="uniqueResourceName" value="mysql/his" />
	<property name="xaDataSourceClassName"
			  value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
	<property name="xaProperties">
		<props>
			<prop key="URL">${his.db.url}</prop>
			<prop key="user">${his.db.user}</prop>
			<prop key="password">${his.db.password}</prop>
			<prop key="pinGlobalTxToPhysicalConnection">true</prop>
		</props>
	</property>
</bean>

<bean id="hispayDataSource" parent="abstractXADataSource">
	<!-- value只要两个数据源不同就行,随便取名 -->
	<property name="uniqueResourceName" value="mysql/hispay" />
	<property name="xaDataSourceClassName"
			  value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
	<property name="xaProperties">
		<props>
			<prop key="URL">${hispay.db.url}</prop>
			<prop key="user">${hispay.db.user}</prop>
			<prop key="password">${hispay.db.password}</prop>
			<prop key="pinGlobalTxToPhysicalConnection">true</prop>
		</props>
	</property>
</bean>


<bean id="hisSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="hisDataSource" />
	<property name="mapperLocations" value="classpath:mybatis/xml/his/*Mapper.xml" />
</bean>

<bean id="hispaySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="hispayDataSource" />
	<property name="mapperLocations" value="classpath:mybatis/xml/hispay/*Mapper.xml" />
</bean>

<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
	  init-method="init" destroy-method="close">
	<!-- atomikosTransactionManager 这个事务管理器可以关闭事务true  --> 
	<property name="forceShutdown">
		<value>true</value>
	</property>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
	<property name="transactionTimeout" value="300" />
</bean>

<bean id="transactionManager"
	  class="org.springframework.transaction.jta.JtaTransactionManager">
	<property name="transactionManager">
		<ref bean="atomikosTransactionManager"/>
	</property>
	<property name="userTransaction">
		<ref bean="atomikosUserTransaction"/>
	</property>
	<!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
	<property name="allowCustomIsolationLevels" value="true"/>

</bean>


 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="cn.edu.his.pay.mapper.his"/>
	<property name="sqlSessionFactoryBeanName" value="hisSqlSessionFactory" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="cn.edu.his.pay.mapper.hispay"/>
	<property name="sqlSessionFactoryBeanName" value="hispaySqlSessionFactory" />
</bean>

<!-- 一定要开启注解事务支持  -->
<tx:annotation-driven transaction-manager="transactionManager"/>

总结:到这里Atomikos集成到SSM框架就算完成了,这篇文章就不进行详细的,对于Atomikos(分布式事务管理框架)的特性,我会在下篇文章中写测试代码去分别测试Atomikos的强大功能。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货

Android项目实战(三十九):Android集成Unity3D项目(图文详解)

356100
来自专栏Java学习网

Android使用Ant进行apk多渠道打包

Android使用Ant进行apk多渠道打包 前言: Ant 是什么? 详细介绍请看http://ant.apache.org/ 总之一句话:Ant是一个Apa...

27950
来自专栏Java架构沉思录

一文读懂数据库事务

什么是事务 根据维基百科的定义,一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:1)为数据库操作序列提供了一个从失败中恢复...

31080
来自专栏云计算教程系列

如何在Ubuntu上安装SELinux

Ubuntu有一个类似于SELinux的强制访问控制系统,名为AppArmor。SELinux(Security-Enhanced Linux) 是美国国家安全...

81510
来自专栏小勇DW3

线上测试环境搭建过程记录

3.安装完以后  会在 /usr/java/latest 下有对应的 jdk 版本

25910
来自专栏Java技术栈

这 30 个常用的 Maven 命令你必须熟悉!

maven 命令的格式为 mvn [plugin-name]:[goal-name],可以接受的参数如下。

12620
来自专栏Java架构沉思录

一文读懂数据库事务

根据维基百科的定义,一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:1)为数据库操作序列提供了一个从失败中恢复到正常状态的方...

45540
来自专栏安恒信息

安全漏洞公告

1 Linux Kernel 'linux-image-3.2.0-4-5kc-malta'软件包拒绝服务漏洞Linux Kernel 'linux-image...

33870
来自专栏Android干货

Android项目实战(三十九):Android集成Unity3D项目(图文详解)

23560
来自专栏云计算教程系列

如何在Ubuntu 16.04上安装Moodle

Moodle是一个流行的,开源的基于Web的学习管理系统(LMS),任何人都可以免费安装和使用。通过Moodle,您可以为学习者群体创建和提供课程,阅读和讨论板...

40000

扫码关注云+社区

领取腾讯云代金券