3.2 读取已提交( Read commited)
读取已提交隔离级别中,读事务不会阻塞任何事务,但是写事务会阻塞 读事务和其它写事务,此时因为写事务会阻塞读事务,所以不会出现“脏读” 问题,但是因为此时读事务还是不阻塞写事务,所以“不可重复读”问题还 会出现,同时“幻影读”问题还是会出现。
3.3 可重复读( Repeatable Read)
可重复读隔离级别中,读事务会阻塞写事务,不阻塞读事务,写事务 会阻塞写事务和读事务,因为此时读事务阻塞了写事务,所以避免了“不可 重复读”的问题,但是此时读事务并没有阻塞对数据库的插入操作,所以此 时“幻影读”问题照样存在。
3.4 序列化 (Serializable)
Serializable 隔离级别是最严格一种隔离级别,数据库系统会保证执行 此种隔离级别事务的效果和顺序执行的效果一致。不过在日常的开发过程种 很少使用此种隔离级别,因为它严重影响了系统的性能。
以上就是事务的四种隔离级别,但是实际的开发当中,我们还是需要注意以下几个问题:
1 并不是所有的数据库都支持以上的四种隔离级别,具体的数据库支持的隔离级别可参考相应的数据库文档。
2 在实际的开发当中,我们经常还会遇到一个问题就是如何在事务隔离级别和系统的并发性方面取得一个折中,如果采用 serializable 隔离级别,这样数据库就做好了并发控制,但是系统性能非常差,此时我们一般采用读取已提交的隔离级别,然后再结合以下几种并发控制的锁定策略:
* 乐观所
* 悲观锁
* 乐观离线锁
* 悲观离线锁
此时其实并发是由应用程序来控制的,而事务的隔离由数据库系统来管理。
二 事务模型解析
首先事务模型分为好几种比如平面事务,嵌套事务等,平面事务是一个完整的不能再进行嵌套的事务,而嵌套型事务容许事务进行嵌套,事务嵌套子事务,这样主事务可以对嵌套子事务进行重试,这样增加了事务成功的总体的成功率,嵌套式事务最典型的例子就是订票。比如某人要去旅行,需要路径 A,B,C , D 四个不同的地方, A 到 B 需要订火车票, B 到 C 需要订机票, C 到 D 需要订船票,那么如果采用平面事务,只要 A,B,C,D 任意一段路程订票失败那么就订票失败了,这样明显成功率比较低,这种情况下可以采用嵌套型事务来避免平面性事务的刚性失败的缺点。
上面说了常见的事务模型的类型,下面主要说一下目前 JAVAEE 应用中,我们常见的事务,本地事务或者 resource-local 事务和全局事务或者说 JTA 事务,需要声明的是 resource-local 和 jta 事务都属于前面说的平面事务模型。在具体说这两种不同类型事务之前,首先我们来明确几个事务管理会涉及到的几个参与者:
1 资源管理器( Resource Manager) :资源管理器一般是数据库管理系统,也可以是消息队列,遗留系统等。
2 分布式事务协调者( Distributed Transaction Coordinator,DTC) 【 1 】:此功能一般是有我们所用的 JavaEE 应用服务器实现,比如 jboss,websphere,weblogic 等。这个角色只有在 JTA 事务中才会存在。
3 事务管理器 (Transaction manager) :每一个事务管理器都与相应的资源管理器所关联,它负责对分布式事务进行提交或者回滚。
4 应用程序( Application)
以上四者的关系可以用以下的图形来形象的表述:
在日常的系统开发中,我们一般都会使用数据资源(比如数据库)来对系统的状态进行保存,那么我们根据系统涉及的数据资源的多少,将事务分为 RESOURCE-LOCAL 事务或者 JTA 全局分布式事务。
1 RESOURCE-LOCAL 事务
RESOURCE-LOCAL 事务是指只有一个资源管理( RM) 的事务,比如 我们系统中只涉及到一个数据库,更准确点说应该是事务操作都是对同一个 数据库进行操作。
此时事务协调着和事务管理器的作用就有底层的资源管理器来实现了。比如目前我们在采用 spring 来管理事务的时候,其实 spring 并没有事务功能,它仅仅是封装了底层数据库的事务操作而已。
2 全局事务或者 JTA 事务
全局事务是涉及多个资源管理器,此时需要引入事务协调者来进行调节,此时就需要两阶段提交协议 2PC ( two phase commit) ,下面简要说一下两阶段提交协议。
第一阶段:事务协调者发送“准备提交”消息给事务所涉及的所有的事务管理器,然后事务管理器又分送此消息给相应的资源管理器,然后事务管理器又将资源管理的响应情况告诉分布式事务协调者( DTC). 只有此阶段顺利完成后(既所有的资源管理器都同意提交事务),才会进入第二阶段。
第二阶段:当第一个阶段顺利完成后,事务协调者告诉事务管理器去提交事务。
我们可以形象的用下图对分布式事务的参与者进行描述:
上图描述一种场景,一个分布式事务中需要操作两个数据库和一个 JMS 服务器,在这种场景下,在进行事务操作的时候,对于 DB 来说,首先事务管理器从支持 XA 接口的 JDBC 驱动程序中取得对应的 javax.transaction.xa.XAResource 实现类, 然后从相应的 JDBC 驱动中获取到 javax.sql.XAConnection 的实现类,其中 XAResource 对应用程序是透明的,应用程序只需要拿到 XAConnection 进行操作数据库即可,对于 JMS 过程类似,首先从支持 XA 的 JMS 消息中间件获取 javax.transaction.xa.XAResource ,然后再获取 javax.jms.XAConnection 的实现类,这里 XAResource 同样对应用程序是透明的。
四:参考文献
1 << 精通 EJB3.0>>
2 <<Enterprise Javabean 3.0>>
3 << 面向模式的软件体系结构 >> 卷 2 ,卷 3
4 << 服务器端组件模式 >>
5 Specification: JSR-000907 Java(tm) Transaction API (JTA) Specification ("Specification"),此文档在附件,有需要的可以下载看看。