螺 丝 钉 阅读(161) 评论(0)

 

事务相关

    本篇文章,大部分摘自网上其他人的博客,由于他们说的都比较分散,这里将他们整合起来。虽然通篇博客,我自己写的内容很少,大部分是从网络上摘自别人的博客,但是我相信在你看了以后,头脑中会有一个清晰的脉络。

 

1、什么是事务?

    事务,指一个程序或程序段,在一个或多个资源如数据库 或文件上为完成某些功能的执行过程的集合。

2、数据库事务

    一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

 

 

3、分布式事务

       分布式系统中,客户的一个请求对应的一组操作。从客户角度看,事务是组成一个活动涉及到的一组操作,它将服务器的数据从一个一致状态,转换为另一个一致性状态。

       也可以说是用户的活动可能涉及到多个服务器的事务。

 

 

 

4、事务的ACID特性

    事务是恢复和并发控制的基本单位。事务具有四个特性,简称为ACID特性:

    ·Atomicity:原子性,一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

    ·Consistency:一致性,事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。  

    ·Isolation:隔离性,多个事务并发执行时,一个事务的执行不应影响其他事务的执行。

    ·Durability:持久性,已被提交的事务对数据库的修改应该永久保存在数据库中。

 

    这四个特性,不论是否为分布式事务,都是适合的。

 

 

5、分布式事务处理

         分布式事务处理(Distributed Transaction Processing  ,  DTP  )是指一个事务可能涉及多个数据库操作,分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)。 

 

 

6、XA DTP模型

6.1 模型说明

       XA DTP模型是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)。

 

 

 

 

其中的资源资源:数据库(Database),消息对象(MQ)等数据资源。

6.2 模型中的角色

上面的模型图中,共有三种角色:AP、TM、RM。

 

TM:事务管理器,负责管理、协调、准备和提交分布式事务,对AP的接口标准为TX接口。

RM:资源管理器,在AP访问数据时,关联事务相关的数据修改,并根据TM的命令提交或回滚数据修改,通常为数据库, IBM的Websphere MQ实现了RM接口。

AP:客户应用程序,负责连接TM,RM,使用RM的提供的API访问和更改数据,声明分布式事务的开始和结束阶段点(Transaction Demarcation)。

 

6.3 XA接口说明

XA Interface:是在XA模型中定义的TM与RM之间的接口()。

XA协议包括两套函数,以xa_开头的及以ax_开头的。

以下的函数使事务管理器可以对资源管理器进行的操作:

1)xa_open,xa_close:建立和关闭与资源管理器的连接。

2)xa_start,xa_end:开始和结束一个本地事务

3)xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。

4)xa_recover:回滚一个已进行预提交的事务。

5)ax_开头的函数使资源管理器可以动态地在事务管理器中进行注册,并可以对XID(TRANSACTION IDS)进行操作。

6)ax_reg,ax_unreg;允许一个资源管理器在一个TMS(TRANSACTION MANAGER SERVER)中动态注册或撤消注册。

 

也就是说:

TM总揽分布式事务处理的全局,

RM是分布式事务处理中的局部处理,

APTMRM之间的连接的桥梁。

 

分布式事务各个阶段相关API调用如下:
1 )AP 通知TM打开RM连接, AP-->TM tx_open()
TM 会在该函数中调用RM提供的xa_open函数,打开到RM的连接。
在TUXEDO SERVICE中,需要在tpsvrinit()函数中调用tpopen()函数完成这项工作。

2 )AP 声明事务开始 AP-->TM tx_begin()
在声明后,该线程后续对RM的所有访问和更新均属于该事务。
对于static RM, TM 需要调用xa_start() 明确关联事务和RM。

在TUXEDO SERVICE/CLIENT中,tpbegin()函数完成类似工作.
当TUXEDO SERVICE被调用时, 如果已经处于事务中, TUXEDO 会自动调用与SERVICE关联的RM的xa_start()函数(只对于 static RM).

3 )AP访问RM,使用RM规定的API访问,XA规范未作定义。
对于dynamic RM, 如果访问时发生了数据更改,例如提交一个UPDATE SQL 语句,  RM会自动回调TM的ax_reg函数关联到当前事务.

4 )AP声明事务分支结束
在TUXEDO SERVICE调用完成后, 自动调用 RM 的xa_end()函数(对于static RM和未调用ax_unreg的dynamic RM)。

说明: 根据业务需要,上述2-4步骤会在不同的进程(TUXEDO SERVICE)中重复出现, 只要事务ID( Global XID )相同,这多个事务分支(Branch) 均被认为属于同一个事务.

5 )AP 要求提交或回滚事务(TM tx_commit/tx_rollback )
AP要求提交事务时, TM 需要检查事务状态, 确定事务并未标记为MARKED_ROLLBACK(只能回滚),否则会回滚并报告错误.

 

 

6.4 两阶段提交协议

 

         XA  接口规范使用两阶段提交协议来完成一个全局事务,保证同一事务中所有数据库同时成功或者回滚。两阶段提交协议假设每个数据库点都存在一个write-ahead log,每一次的write请求都是先记log后才真正执行写入。

 

第一篇博客上拷贝的内容:

摘自:

http://blog.sina.com.cn/s/blog_ab62f6e801015wpb.html

第一阶段为提交请求阶段(Commit-request phase):
   1. 事务管理器给所有数据库发query to commit消息请求,然后开始等待回应;
   2. 数据库如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来(在undo log和redo log中各记一项);
   3. 数据库都回应是否同意提交的应答。
第二阶段为提交阶段(Commit phase):
如果事务管理器收到的所有回应都是agreement,
   1. 事务管理器记日志并给所有数据库发commit消息请求;
   2. 各个数据库执行操作,释放所有该事务相关的锁和资源;
   3. 各个数据库给事务管理器回复;
   4.当收到所有回复,事务管理器结束当前事务

如果事务管理器收到的任一回应是abort,
   1. 事务管理器记日志并给所有数据库发rollback消息请求;
   2. 各个数据库执行undo操作,释放所有该事务相关的锁和资源;
   3. 各个数据库给事务管理器回复;
   4.当收到所有回复,事务管理器结束当前事务

两 阶段提交协议的问题在于数据库在提交请求阶段应答后对很多资源处于锁定状态,要等到事务管理器收集齐所有数据库的应答后,才能发commit或者 rollback消息结束这种锁定。锁定时间的长度是由最慢的一个数据库制约,如果数据库一直没有应答,所有其他库也需要无休止的锁并等待。并且,如果事 务管理器出现故障,被锁定的资源将长时间处于锁定状态。无论是任一数据库或者事务管理器故障,其他数据库都需要永久锁定或者至少长时间锁定。并且,分布式 系统中节点越多,存在缓慢网络或者故障节点的概率也就越大,资源被长时间锁定的概率指数上升。
两阶段提交协议的另一个问题是只要有任意一个数据库 不可用都会导致事务失败,这导致事务更倾向于失败。对于多个副本的备份系统,很多时候我们希望部分副本点失效时系统仍然可用,使用该协议则不能实现。并 且,分布式系统中节点越多,存在故障节点的概率也就越大,系统的可用性指数下降。
另外,如果数据库在第一阶段应答后到第二阶段正式提交前的某个阶段网络故障或者节点故障,该协议无法提交或回滚,数据不一致不能绝对避免。

 

从另一篇博客上拷贝的:

摘自:http://www.blogjava.net/rednight/archive/2007/03/06/102070.html

通常情况下,交易中间件与数据库通过 XA 接口规范,使用两阶段提交来完成一个全局事务, XA 规范的基础是两阶段提交协议。
  在第一阶段,交易中间件请求所有相关数据库准备提交(预提交)各自的事务分支,以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预提交后,如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来,并给交易中间件一个同意提交的应答,此时数据库将不能再在该事务分支中加入任何操作,但此时数据库并没有真正提交该事务,数据库对共享资源的操作还未释放(处于上锁状态)。如果由于某种原因数据库无法提交属于自己的事务分支,它将回滚自己的所有操作,释放对共享资源上的锁,并返回给交易中间件失败应答。 在第二阶段,交易中间件审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。 
  以一个全局事务为例, AP 首先通知交易中间件开始一个全局事务,交易中间件通过 XA 接口函数通知数据库开始事务,然后 AP 可以对数据库管理的资源进行操作,数据库系统记录事务对本地资源的所有操作。操作完成后交易中间件通过 XA 接口函数通知数据库操作完成。交易中间件负责记录 AP 操作过哪些数据库(事务分支)。 AP 根据情况通知交易中间件提交该全局事务,交易中间件会通过 XA 接口函数要求各个数据库做预提交,所有数据库返回成功后要求各个数据库做正式提交,此时一笔全局事务结束。 
   XA 规范对应用来说,最大好处在于事务的完整性由交易中间件和数据库通过 XA 接口控制, AP 只需要关注与数据库的应用逻辑的处理,而无需过多关心事务的完整性,应用设计开发会简化很多。   具体来说,如果没有交易中间件,应用系统需要在程序内部直接通知数据库开始、结束和提交事务,当出现异常情况时必须由专门的程序对数据库进行反向操作才能完成回滚。如果是有很多事务分支的全局事务,回滚时情况将变得异常复杂。而使用 XA 接口,则全局事务的提交是由交易中间件控制,应用程序只需通知交易中间件提交或回滚事务,就可以控制整个事务(可能涉及多个异地的数据库)的全部提交或回滚,应用程序完全不用考虑冲正逻辑。 
  在一个涉及多个数据库的全局事务中,为保证全局事务的完整性,由交易中间件控制数据库做两阶段提交是必要的。但典型的两阶段提交,对数据库来说事务从开始到结束(提交或回滚)时间相对较长,在事务处理期间数据库使用的资源(如逻辑日志、各种锁),直到事务结束时才会释放。因此,使用典型的两阶段提交相对来说会占用更多的资源,在网络条件不是很好,如低速网、网络颠簸频繁,情况会更为严重。 
  当一个全局事务只涉及一个数据库时,有一种优化方式,即一阶段提交。当 AP 通知交易中间件提交事务时,交易中间件直接要求数据库提交事务,省去两阶段提交中的第一阶段,可以缩短处理一个事务的时间,以提高事务处理的效率。作为两阶段提交的一种特例,与两阶段一样,一阶段提交也是标准的。

 

第三篇博客上拷贝内容

 

摘自:

http://blog.csdn.net/bluishglc/article/details/7612811

 

所有关于分布式事务的介绍中都必然会讲到两阶段提交,因为它是实现XA分布式事务的关键(确切地说:两阶段提交主要保证了分布式事务的原子性:即所有结点要么全做要么全不做)。所谓的两个阶段是指:第一阶段:准备阶段和第二阶段:提交阶段。

 

 

 

1.准备阶段:事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的redo和undo日志,但不提交,到达一种“万事俱备,只欠东风”的状态。(关于每一个参与者在准备阶段具体做了什么目前我还没有参考到确切的资料,但是有一点非常确定:参与者在准备阶段完成了几乎所有正式提交的动作,有的材料上说是进行了“试探性的提交”,只保留了最后一步耗时非常短暂的正式提交操作给第二阶段执行。)

2.提交阶段:如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)

将提交分成两阶段进行的目的很明确,就是尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是一个耗时极短的微小操作,这种操作在一个分布式系统中失败的概率是非常小的,也就是所谓的“网络通讯危险期”非常的短暂,这是两阶段提交确保分布式事务原子性的关键所在。(唯一理论上两阶段提交出现问题的情况是当协调者发出提交指令后当机并出现磁盘故障等永久性错误,导致事务不可追踪和恢复)

从两阶段提交的工作方式来看,很显然,在提交事务的过程中需要在多个节点之间进行协调,而各节点对锁资源的释放必须等到事务最终提交时,这样,比起一阶段提交,两阶段提交在执行同样的事务时会消耗更多时间。事务执行时间的延长意味着锁资源发生冲突的概率增加,当事务的并发量达到一定数量的时候,就会出现大量事务积压甚至出现死锁,系统性能就会严重下滑。这就是使用XA事务。

 

 

对于这一节,也可以参考:

http://www.cnblogs.com/aigongsi/archive/2012/10/11/2718313.html

 

7、全局事务与局部事务

 

全局事务与局部事务的相关内容摘自博客:

http://blog.sina.com.cn/s/blog_4fb1c0fc0100aq8x.html

这篇博客其实是对Spring的参考文档中的翻译。先把Spring参考文档中的内容粘出来:

 

Traditionally, Java EE developers have had two choices for transaction management: global or local transactions, both of which have profound limitations. Global and local transaction management is reviewed in the next two sections, followed by a discussion of how the Spring Framework's transaction management support addresses the limitations of the global and local transaction models.

11.2.1 Global transactions

Global transactions enable you to work with multiple transactional resources, typically relational databases and message queues. The application server manages global transactions through the JTA, which is a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be sourced from JNDI, meaning that you also need to use JNDI in order to use JTA. Obviously the use of global transactions would limit any potential reuse of application code, as JTA is normally only available in an application server environment.

Previously, the preferred way to use global transactions was via EJB CMT (Container Managed Transaction): CMT is a form of declarative transaction management (as distinguished from programmatic transaction management). EJB CMT removes the need for transaction-related JNDI lookups, although of course the use of EJB itself necessitates the use of JNDI. It removes most but not all of the need to write Java code to control transactions. The significant downside is that CMT is tied to JTA and an application server environment. Also, it is only available if one chooses to implement business logic in EJBs, or at least behind a transactional EJB facade. The negatives of EJB in general are so great that this is not an attractive proposition, especially in the face of compelling alternatives for declarative transaction management.

11.2.2 Local transactions

Local transactions are resource-specific, such as a transaction associated with a JDBC connection. Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Because the application server is not involved in transaction management, it cannot help ensure correctness across multiple resources. (It is worth noting that most applications use a single transaction resource.) Another downside is that local transactions are invasive to the programming model.

 
  • 全局事务 - 资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用 XA 二阶段提交协议与“企业信息系统”(EIS) 或数据库进行交互。
  • 本地事务 - 在单个 EIS 或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。

 

 

全局事务 全局事务有一个重大的缺陷,代码需要使用JTA:一个笨重的API(部分是因为它的异常模型)。此外,JTA的UserTransaction通常需要从JNDI获得,这意味着我们为了JTA,需要 同时 使用JNDI  JTA。显然全部使用全局事务限制了应用代码的重用性,因为JTA通常只在应用服务器的环境中才能使用。 以前,使用全局事务的首选方式是通过EJB的 CMT容器管理事务):CMT是 声明式事务管理 的一种形式(区别于 编程式事务管理)。EJB的CMT不需要任何和事务相关的JNDI查找,虽然使用EJB本身肯定需要使用JNDI。它消除了大多数(不是全部)硬编码的方式去控制事务。重大的缺陷是CMT绑定在JTA和应用服务器环境上,并且只有我们选择使用EJB实现业务逻辑,或者至少处于一个事务化EJB的外观(Facade)后才能使用它。EJB有如此多的诟病,尤其是存在其它声明式事务管理时,EJB不是一个吸引人的建议。

本地事务本地事务容易使用,但也有明显的缺点:它们不能用于多个事务性资源。例如,使用JDBC连接事务管理的代码不能用于全局的JTA事务中。另一个缺点是局部事务趋向于入侵式的编程模型。

 

 

 

 

8、JTA简述

 

了解JTA,可以参考:

http://www.bccn.net/Article/kfyy/java/jszl/200709/6154.html

http://zh.wikipedia.org/wiki/Java事务API

http://www.ibm.com/developerworks/cn/java/j-lo-jta/

在看这三篇博客时,我相信你会对之前的内容有更深的理解;同时,如果看这三篇博客前,建议好好的看一看前面456小节的内容。

 

 

9、局部事务

    在上面几个小节中,了解了全局事务与局部事务。这里专门说一下局部事务。

所谓的局部事务,其实就是JDBC编程中的事务。

默认情况下,JDBC中是Connection.setAutoCommt(true),也就是说,事务是自动提交的。每执行一个SQL就会自动提交事务。

当然了也可以设置为false,自己在代码中控制事务的提交,例如:

Connection conn = getConnection();

        conn.setAutoCommit(false);

        // do something

        boolean success = doSomething();

        if (success) {

            conn.commit();

        } else {

            conn.rollback();

        }

 

 


10、编程式事务与声明式事务 

    在使用Spring编程中,为局部事务提供了两种方式:编程式事务和声明式事务。不论哪种,都是局部事务,也就是说,都要经历begin\commit(或者rollback)的事务处理。两者的区别,就是在于对编程式和声明式的理解上。

    编程式事务:就是在Spring环境下,使用Java代码编程的方式来控制事务。例如使用TransactionTemplate或者使用PlatformTransactionManager来进行编程。

    声明式事务:就是在Spring环境下,使用AOP方式进行事务的声明配置。这样不用在编程时使用事务相关的代码。