java分布式事务代码 java分布式事务解决方案
java程序员在面试中被问到如何配置多数据源以及如何配置多数据源下的分布式事务,该怎么回答?看清再做答
你好,我来先回答你的第一个问题:
成都创新互联专注于洛阳网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供洛阳营销型网站建设,洛阳网站制作、洛阳网页设计、洛阳网站官网定制、成都微信小程序服务,打造洛阳网络公司原创品牌,更为您提供洛阳网站排名全网营销落地服务。
通常多数据源,在spring中配置如下,如果你想切换环境ENV 的值,在property中
bean id="placeholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
property name="ignoreResourceNotFound" value="true"/property
property name="ignoreUnresolvablePlaceholders" value="true"/property
property name="nullValue" value="NULL"/property
property name="locations"
list
valuejdbc.properties/value
/list
/property
/bean
bean id="dataSource" class="com.spring.dao.JDBCConfig"
property name="driverClassName" value="${${Env}.jdbc.driverClassName}"/property
property name="url" value="${${Env}.jdbc.url}"/property
property name="username" value="${${Env}.jdbc.username1}"/property
property name="password" value="${${Env}.jdbc.password}"/property
/bean
jdbc.properties
*****************************
Env=PROD
jdbc.driverClassName=${${Env}.jdbc.driverClassName}
jdbc.url=${${Env}.jdbc.url}
jdbc.username=${${Env}.jdbc.username}
jdbc.password=${${Env}.jdbc.password}
######### JDBC Configuration for DEV Environment ###############
DEV.jdbc.driverClassName=com.mysql.jdbc.Driver
DEV.jdbc.url=jdbc:mysql://localhost:3306/devportal
DEV.jdbc.username=DEVuser
DEV.jdbc.password=DEVpwd
######### JDBC Configuration for UAT Environment ############
UAT.jdbc.driverClassName=com.mysql.jdbc.Driver
UAT.jdbc.url=jdbc:mysql://localhost:3306/UATportal
UAT.jdbc.username=UATuser
UAT.jdbc.password=UATpwd
########## JDBC Configuration for PROD Environment ############
PROD.jdbc.driverClassName=com.mysql.jdbc.Driver
PROD.jdbc.url=jdbc:mysql://localhost:3306/portal
PROD.jdbc.username=root
PROD.jdbc.password=admin,
我这里有三套环境,分别是DEV,UAT和PROD,这种方式可以灵活切换的。
我再回答你的第二个问题:
还请你去这里看下,很详细,不过是英文的哦
java的框架spring如何配置分布式事务?
分布式事务是指操作多个数据库之间的事务,在tomcat下是没有分布式事务的,可以借助于第三方Jotm和Automikos实现,下面就写一个使用Jotm实现分布事务的例子,如有不足,请各位大大指点:
Dao及实现,先写出一个interface再去实现他,可能有些人觉得直接写实现类多好,但我还是建议为了结构清晰,增强代码的可读性,可维护性还是先写接口再去实现的好:
先写一个interface,定义要实现的方法:
实现接口,传入一个String ds来判断调用哪个JdbcTemplate:
service及实现:
还是接口与他的实现:
持久化的操作:
applicationContext.xml
基本的spring配置以及Jotm bean;
JTA事务管理器,数据源datasourceA和datasourceB配置:
事务切面配置aop,通知配置以及dao,service配置:
单元测试,在实际项目中就是写一个controller:
Java分布式系统处理分布式事务有哪些经典解决方
当我们在生产线上用一台服务器来提供数据服务的时候,我会遇到如下的两个问题:
1)一台服务器的性能不足以提供足够的能力服务于所有的网络请求。
2)我们总是害怕我们的这台服务器停机,造成服务不可用或是数据丢失。
于是我们不得不对我们的服务器进行扩展,加入更多的机器来分担性能上的问题,以及来解决单点故障问题。 通常,我们会通过两种手段来扩展我们的数据服务:
1)数据分区:就是把数据分块放在不同的服务器上(如:uid % 16,一致性哈希等)。
2)数据镜像:让所有的服务器都有相同的数据,提供相当的服务。
对于第一种情况,我们无法解决数据丢失的问题,单台服务器出问题时,会有部分数据丢失。所以,数据服务的高可用性只能通过第二种方法来完成——数据的冗余存储(一般工业界认为比较安全的备份数应该是3份,如:Hadoop和Dynamo)。 但是,加入更多的机器,会让我们的数据服务变得很复杂,尤其是跨服务器的事务处理,也就是跨服务器的数据一致性。这个是一个很难的问题。 让我们用最经典的Use Case:“A帐号向B帐号汇钱”来说明一下,熟悉RDBMS事务的都知道从帐号A到帐号B需要6个操作:
从A帐号中把余额读出来。
对A帐号做减法操作。
把结果写回A帐号中。
从B帐号中把余额读出来。
对B帐号做加法操作。
把结果写回B帐号中。
为了数据的一致性,这6件事,要么都成功做完,要么都不成功,而且这个操作的过程中,对A、B帐号的其它访问必需锁死,所谓锁死就是要排除其它的读写操作,不然会有脏数据的问题,这就是事务。那么,我们在加入了更多的机器后,这个事情会变得复杂起来:
1)在数据分区的方案中:如果A帐号和B帐号的数据不在同一台服务器上怎么办?我们需要一个跨机器的事务处理。也就是说,如果A的扣钱成功了,但B的加钱不成功,我们还要把A的操作给回滚回去。这在跨机器的情况下,就变得比较复杂了。
2)在数据镜像的方案中:A帐号和B帐号间的汇款是可以在一台机器上完成的,但是别忘了我们有多台机器存在A帐号和B帐号的副本。如果对A帐号的汇钱有两个并发操作(要汇给B和C),这两个操作发生在不同的两台服务器上怎么办?也就是说,在数据镜像中,在不同的服务器上对同一个数据的写操作怎么保证其一致性,保证数据不冲突?
同时,我们还要考虑性能的因素,如果不考虑性能的话,事务得到保证并不困难,系统慢一点就行了。除了考虑性能外,我们还要考虑可用性,也就是说,一台机器没了,数据不丢失,服务可由别的机器继续提供。 于是,我们需要重点考虑下面的这么几个情况:
1)容灾:数据不丢、节点的Failover
2)数据的一致性:事务处理
3)性能:吞吐量 、 响应时间
前面说过,要解决数据不丢,只能通过数据冗余的方法,就算是数据分区,每个区也需要进行数据冗余处理。这就是数据副本:当出现某个节点的数据丢失时可以从副本读到,数据副本是分布式系统解决数据丢失异常的唯一手段。所以,在这篇文章中,简单起见,我们只讨论在数据冗余情况下考虑数据的一致性和性能的问题。简单说来:
1)要想让数据有高可用性,就得写多份数据。
2)写多份的问题会导致数据一致性的问题。
3)数据一致性的问题又会引发性能问题
这就是软件开发,按下了葫芦起了瓢。
一致性模型
说起数据一致性来说,简单说有三种类型(当然,如果细分的话,还有很多一致性模型,如:顺序一致性,FIFO一致性,会话一致性,单读一致性,单写一致性,但为了本文的简单易读,我只说下面三种):
1)Weak 弱一致性:当你写入一个新值后,读操作在数据副本上可能读出来,也可能读不出来。比如:某些cache系统,网络游戏其它玩家的数据和你没什么关系,VOIP这样的系统,或是百度搜索引擎(呵呵)。
2)Eventually 最终一致性:当你写入一个新值后,有可能读不出来,但在某个时间窗口之后保证最终能读出来。比如:DNS,电子邮件、Amazon S3,Google搜索引擎这样的系统。
3)Strong 强一致性:新的数据一旦写入,在任意副本任意时刻都能读到新值。比如:文件系统,RDBMS,Azure Table都是强一致性的。
从这三种一致型的模型上来说,我们可以看到,Weak和Eventually一般来说是异步冗余的,而Strong一般来说是同步冗余的,异步的通常意味着更好的性能,但也意味着更复杂的状态控制。同步意味着简单,但也意味着性能下降。 好,让我们由浅入深,一步一步地来看有哪些技术:
Master-Slave
首先是Master-Slave结构,对于这种加构,Slave一般是Master的备份。在这样的系统中,一般是如下设计的:
1)读写请求都由Master负责。
2)写请求写到Master上后,由Master同步到Slave上。
从Master同步到Slave上,你可以使用异步,也可以使用同步,可以使用Master来push,也可以使用Slave来pull。 通常来说是Slave来周期性的pull,所以,是最终一致性。这个设计的问题是,如果Master在pull周期内垮掉了,那么会导致这个时间片内的数据丢失。如果你不想让数据丢掉,Slave只能成为Read-Only的方式等Master恢复。
当然,如果你可以容忍数据丢掉的话,你可以马上让Slave代替Master工作(对于只负责计算的节点来说,没有数据一致性和数据丢失的问题,Master-Slave的方式就可以解决单点问题了) 当然,Master Slave也可以是强一致性的, 比如:当我们写Master的时候,Master负责先写自己,等成功后,再写Slave,两者都成功后返回成功,整个过程是同步的,如果写Slave失败了,那么两种方法,一种是标记Slave不可用报错并继续服务(等Slave恢复后同步Master的数据,可以有多个Slave,这样少一个,还有备份,就像前面说的写三份那样),另一种是回滚自己并返回写失败。(注:一般不先写Slave,因为如果写Master自己失败后,还要回滚Slave,此时如果回滚Slave失败,就得手工订正数据了)你可以看到,如果Master-Slave需要做成强一致性有多复杂。
Master-Master
Master-Master,又叫Multi-master,是指一个系统存在两个或多个Master,每个Master都提供read-write服务。这个模型是Master-Slave的加强版,数据间同步一般是通过Master间的异步完成,所以是最终一致性。 Master-Master的好处是,一台Master挂了,别的Master可以正常做读写服务,他和Master-Slave一样,当数据没有被复制到别的Master上时,数据会丢失。很多数据库都支持Master-Master的Replication的机制。
另外,如果多个Master对同一个数据进行修改的时候,这个模型的恶梦就出现了——对数据间的冲突合并,这并不是一件容易的事情。看看Dynamo的Vector Clock的设计(记录数据的版本号和修改者)就知道这个事并不那么简单,而且Dynamo对数据冲突这个事是交给用户自己搞的。就像我们的SVN源码冲突一样,对于同一行代码的冲突,只能交给开发者自己来处理。(在本文后后面会讨论一下Dynamo的Vector Clock)
Two/Three Phase Commit
这个协议的缩写又叫2PC,中文叫两阶段提交。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。
如何用java开启mysql事务,要求详细
看你是什么事务,jdbc事务,还是分布式事务,还是容器事务
1,编程式事务管理(jdbc的事务是绑定在connection上的)
Connection conn = null;
try
{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","password");
conn.setAutoCommit(false); //取消自动提交
PreparedStatement ps = conn.prepareCall("update something");
ResultSet rs = ps.executeQuery();
conn.commit(); //手动提交
}
catch (Exception e)
{
conn.rollback();
e.printStackTrace();
}
finally
{
conn.close();
}
2,声明式事务
先在工程的application.xml配置文件中添加如下代码,开启事务
!-- 声明式事务控制配置 --
tx:annotation-driven transaction-manager="txManager"/
bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
property name="datasource" ref="bassDataSource"/property
/bean
然后在你需要开启事务的接口前面添加注解
@Transactional(rollbackFor = IOException.class)
public void add(String name) throws IOException
{
System.out.println("可以再类里和方法里面添加事务注解0~0");
throw new IOException();
}
直接调用接口方法就好
分布式事务处理(mysql貌似在5.X之后才支持) 的话,
1.可以直接使用spring+atomikos框架进行管理
参考:
就不贴测试代码了,自己看着配置吧
2,使用JTA(Java Transaction API)进行分布式事务管理(测试代码如下)
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
//分布式事务处理
public class transferAccount
{
@SuppressWarnings("null")
public void testTransferAccount()
{
UserTransaction userts = null;
Connection connA = null;
PreparedStatement psA = null;
InitialContext context = null;
Connection connB = null;
PreparedStatement psB = null;
try
{
//获得事务管理对象
userts = (UserTransaction) context.lookup("java:comp/UserTransaction");
//获取两个数据库
connA = getDataSourceA().getConnection();
connB = getDataSourceB().getConnection();
//开启事务
userts.begin();
//sql语句
psA = connA.prepareStatement("我加1");
psB = connB.prepareStatement("我减1");
//执行sql
psA.executeUpdate();
psB.executeUpdate();
//事务提交
userts.commit();
} catch (Exception e)
{
try
{
userts.rollback();
} catch (IllegalStateException | SecurityException
| SystemException e1)
{
e1.printStackTrace();
}
e.printStackTrace();
}
finally
{
try
{
psA.close();
psB.close();
connA.close();
connB.close();
} catch (SQLException e)
{
e.printStackTrace();
}
}
}
public DataSource getDataSourceA()
{
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("mysql");
dataSource.setServerName("server");
dataSource.setPortNumber(1433);
dataSource.setUser("test");
dataSource.setPassword("test");
return dataSource;
}
public DataSource getDataSourceB()
{
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("mysql");
dataSource.setServerName("server");
dataSource.setPortNumber(1435);
dataSource.setUser("test1");
dataSource.setPassword("test1");
return dataSource;
}
}
文章题目:java分布式事务代码 java分布式事务解决方案
文章路径:http://azwzsj.com/article/doshpoe.html