1、什么是事务
事务是访问并可能更新数据资源(如数据库、文件系统等)中各种数据项的一个程序执行单元。如一个系统对数据资源进行访问时,为了保证系统始终处于正确状态,必须对数据资源的访问操作上进行一些必要的限定 。因此事务就是以可控的方式对数据资源访问的一组操作。事务本身有4个限定属性:
1.1 原子性(Atomicity):要求事务所包含的操作是一个不可分割的整体,要么全都提交成功,要么只要其中一步操作失败就全部失败。
1.2 一致性(Consistency):要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于某个数据一致性状态,那么事务执行之后也依然需要保持数据间的一致性状态。比如银行取钱,余额会相应地减少,手头的钱也会增加,取钱之前和取钱之后资产总数是不变的。
1.3隔离性(Isolation):事务的隔离性规定了各个事务之间相互影响的程度。隔离性概念主要面对数据资源的并发访问,并兼顾影响事务的一致性。因此有4中隔离级别,由弱到强分别为:
1.3.1 Read Uncommitted:一个事务可以读取另一个事务没有提交的更新结果。隔离级别最弱当然性能也是最高的。其无法避免以下问题:
1.3.1.1 脏读(Dirty Read)
如果一个事务对数据进行了更新但没有提交,另一个事务可以读取到该事务的没有提交的结果。可能造成的问题是如果该事务回滚了,另一个事务读取的数据就是“脏数据”。
1.3.1.2 不可重复读取(Non-Repeatable Read)
指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。例如事务1在事务2 的更新操作之前读取的数据与在事务2更新操作后再次读取的数据是不一样的。
1.3.1.3 幻读(Phantom Read)
指同一个查询在同一个事务过程中多次执行后,查询所得的结果集是不同的。
1.3.2 Read Committed
是大多数数据默认采用的隔离级别,一个事务的更新结果只有在提交后才能被另一个事务所读取,因此该隔离级别只能避免脏读。
1.3.3 Repeatable Read
保证在整个事务中对同一笔数据的读取结果是一样的,不管其他事务是否同时对一笔数据进行更新,不管其他事务对同一笔数据的更新提交与否。其避免了脏读和不可重复读。
1.3.4 Serializable
最严格的隔离级别,所有事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的问题,但同时也是性能最差的隔离级别。通常很少使用,一般在其他隔离级别上加上相应的并发锁机制来控制对数据资源的访问,这样能保证性能不会损失太大。
1.4 持久性(Durability):指一旦整个事务操作成功提交,对数据所做的变更将被持久化并不可逆转
2. 传播行为
ServiceA { void methodA() { ServiceB.methodB(); } } ServiceB { void methodB() { } }
1: PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚
2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行这就跟平常用的普通非事务的代码只有一点点区别了。不理这个,因为我也没有觉得有什么区别
3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。
7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
ServiceA { void methodA() { try { //savepoint ServiceB.methodB(); //PROPAGATION_NESTED 级别 } catch (SomeException) { // 执行其他业务, 如 ServiceC.methodC(); } } }
3.事务的参与者
2.1 Resource Manager :简称RM,负责存储并管理系统数据资源的状态,如数据库服务器、JMS消息服务都是相应的RM。
2.2 Transaction Processing Monitor:简称TPM,职责是在分布式事务场景中协调包含多个RM的事务处理。通常,J2EE规范中的应用服务器担任的就是TPM。
2.3 Transaction Manager:简称TM,直接负责RM之间的事务处理的协调工作,并提供事务界定,事务上下文传播等接口。
2.4 Application:以独立形式存在的或者运行于容器中的应用程序。