分布式事务和原子性
分布式事务(distributed transaction) 指的是包含多个事务资源的事务。事务资源指的是比如和关系型数据库和消息中间件通讯的连接器。通常这样的资源提供一些API比如begin(), rollback(), commit(). 在Java编程中, 事务资源通常显露为一个由底层平台提供的工厂产品:对于数据库而言,它是一个由DataSource产生的连接,或者Java Persistence API (JPA) EntityManager;对于Java Message Service (JMS)而言,它是一个会话(Session).
典型的一个用例, JMS消息触发一个数据库更新。一个成功的交互序列如下所示:
开始一个消息事务
获取消息
开始数据库事务
更新数据库
提交数据库事务
提交消息事务
如果一个数据库错误比如在更新时遇到约束冲突,理想的交互序列看起来如下
1. 开始消息事务
2. 接收消息
3. 开始数据库事务
4. 更新数据库, 失败!
5. 回滚数据库事务
6. 回滚消息事务
在 JAVA 中要想使用分布式事务处理,需要使用 JTA。但是 JTA 在 J2SE 环境中是没办法测试的,必须在 J2EE 应用服务器中。
在 J2EE 环境中使用 JTA 事务与 Local 事务的代码是一模一样的,对于开发人员来说这绝对是希望听到的。Local 事务就是通常所称的 Connection 的事务。
分布式事务一般采用一种称为 2-PC(两阶段提交)的协议进行处理,2-PC 是分布式事务处理协议。
比如说一个事务涉及 Oracle、MySQL 和 MS SQLServer 三个数据库的操作,举个最简单的例子,要在这三个数据库中各插入一条数据,但必须保持在一个事务中,要三个插入全部成功才算成功,如果只成功了一个或者两个,那么所有的操作都进行回滚,而 2-PC 就是用来干这事的。
两阶段提交需要有个中间协调人。在 Java 中只有 JTA 才能支持两阶段提交,而这个中间协调人就是 J2EE 应用服务器。
继续刚才那个事务,两个阶段如下:
一、各数据库在执行完 INSERT 后,J2EE 应用服务器在收到提交指令,这时通知各数据库进行事务提交准备。
数据库在收到响应后,进行准备工作,基本上是一个预提交工作,如果能提交则响应 J2EE 应用服务器是 能成功提交的,如果无法提交则响应 J2EE 应用服务器是无法提交的。
二、J2EE 应用服务器在收集到所有的响应之后进行判断,如果在第一阶段收到的信息都是可提交的,那么就通知所有的数据库进行提交;如果在第一阶段收到的信息有一个是无法提交的,那么就通知所有的数据库进行回滚操作。
通过这些步骤,可以看出分布式事务处理是很耗时的,也是相当麻烦的。因为数据库在第一阶段给事务协调器响应后如果能提交,在第二阶段就必须要保证事务能被提交,这是数据库要做的事情。
这里的 J2EE 应用服务器是 2-PC 的协调者。2-PC 在 JAVA 中不仅可以用于分布式数据库事务,也能应用于 JMS 事务。
要支持分布式事务,那么数据库就必须支持两阶段提交协议,否则是不能支持的。
例: 假设a对象为mysql数据源对象,b对象MS,c对象为oracle
try{
a.beginTransaction();
//业务操作 insert
a.execute();
b.beginTransaction();
//业务操作 insert
b.execute();
c.beginTransaction();
//业务操作 insert
c.execute();
c.commit();
b.commit();
a.commit();
}catch(Exception e){
//全回滚
c.rollback();
b.rollback();
a.rollback();
}
finally{
c.close();
b.close();
a.close();
}
总结:
分布式事务,常见的两个处理办法就是两阶段式提交和补偿。
两段式提交典型的就是XA,有个事务协调器,告诉大家,来都准备好提交,大家回复,都准备好了,然后协调器告诉大家,一起提交,大家都提交了。
补偿比较好理解,先处理业务,然后定时或者回调里,检查状态是不是一致的,如果不一致采用某个策略,强制状态到某个结束状态(一般是失败状态),然后就Ok了。典型的就是冲正操作。
注:XA 接口规范 使用两阶段提交协议来完成一个全局事务,保证同一事务中所有数据库同时成功或者回滚。
分布式事务(distributed transaction) 指的是包含多个事务资源的事务。事务资源指的是比如和关系型数据库和消息中间件通讯的连接器。通常这样的资源提供一些API比如begin(), rollback(), commit(). 在Java编程中, 事务资源通常显露为一个由底层平台提供的工厂产品:对于数据库而言,它是一个由DataSource产生的连接,或者Java Persistence API (JPA) EntityManager;对于Java Message Service (JMS)而言,它是一个会话(Session).
典型的一个用例, JMS消息触发一个数据库更新。一个成功的交互序列如下所示:
开始一个消息事务
获取消息
开始数据库事务
更新数据库
提交数据库事务
提交消息事务
如果一个数据库错误比如在更新时遇到约束冲突,理想的交互序列看起来如下
1. 开始消息事务
2. 接收消息
3. 开始数据库事务
4. 更新数据库, 失败!
5. 回滚数据库事务
6. 回滚消息事务
在 JAVA 中要想使用分布式事务处理,需要使用 JTA。但是 JTA 在 J2SE 环境中是没办法测试的,必须在 J2EE 应用服务器中。
建议,在 J2EE 容器上绑定 XA 的 DataSource,并且绑定 JTA 事务。在其他各应用中均使用 JNDI 从 J2EE 容器中获取连接,并开启事务。
在 J2EE 环境中使用 JTA 事务与 Local 事务的代码是一模一样的,对于开发人员来说这绝对是希望听到的。Local 事务就是通常所称的 Connection 的事务。
分布式事务一般采用一种称为 2-PC(两阶段提交)的协议进行处理,2-PC 是分布式事务处理协议。
比如说一个事务涉及 Oracle、MySQL 和 MS SQLServer 三个数据库的操作,举个最简单的例子,要在这三个数据库中各插入一条数据,但必须保持在一个事务中,要三个插入全部成功才算成功,如果只成功了一个或者两个,那么所有的操作都进行回滚,而 2-PC 就是用来干这事的。
两阶段提交需要有个中间协调人。在 Java 中只有 JTA 才能支持两阶段提交,而这个中间协调人就是 J2EE 应用服务器。
继续刚才那个事务,两个阶段如下:
一、各数据库在执行完 INSERT 后,J2EE 应用服务器在收到提交指令,这时通知各数据库进行事务提交准备。
数据库在收到响应后,进行准备工作,基本上是一个预提交工作,如果能提交则响应 J2EE 应用服务器是 能成功提交的,如果无法提交则响应 J2EE 应用服务器是无法提交的。
二、J2EE 应用服务器在收集到所有的响应之后进行判断,如果在第一阶段收到的信息都是可提交的,那么就通知所有的数据库进行提交;如果在第一阶段收到的信息有一个是无法提交的,那么就通知所有的数据库进行回滚操作。
通过这些步骤,可以看出分布式事务处理是很耗时的,也是相当麻烦的。因为数据库在第一阶段给事务协调器响应后如果能提交,在第二阶段就必须要保证事务能被提交,这是数据库要做的事情。
这里的 J2EE 应用服务器是 2-PC 的协调者。2-PC 在 JAVA 中不仅可以用于分布式数据库事务,也能应用于 JMS 事务。
要支持分布式事务,那么数据库就必须支持两阶段提交协议,否则是不能支持的。
例: 假设a对象为mysql数据源对象,b对象MS,c对象为oracle
try{
a.beginTransaction();
//业务操作 insert
a.execute();
b.beginTransaction();
//业务操作 insert
b.execute();
c.beginTransaction();
//业务操作 insert
c.execute();
c.commit();
b.commit();
a.commit();
}catch(Exception e){
//全回滚
c.rollback();
b.rollback();
a.rollback();
}
finally{
c.close();
b.close();
a.close();
}
总结:
分布式事务,常见的两个处理办法就是两阶段式提交和补偿。
两段式提交典型的就是XA,有个事务协调器,告诉大家,来都准备好提交,大家回复,都准备好了,然后协调器告诉大家,一起提交,大家都提交了。
补偿比较好理解,先处理业务,然后定时或者回调里,检查状态是不是一致的,如果不一致采用某个策略,强制状态到某个结束状态(一般是失败状态),然后就Ok了。典型的就是冲正操作。
注:XA 接口规范 使用两阶段提交协议来完成一个全局事务,保证同一事务中所有数据库同时成功或者回滚。