事务特性,事务的隔离级别以及spring中定义的事务传播行为

什么是事务?

事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。

事务特性

原子性 (atomicity):强调事务的不可分割,组成事务操作的各个最小逻辑单元缺一不可,强调了 操作的完整性

一致性 (consistency):事务的执行前后,数据的完整性保持一致,强调 数据的完整性

隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰,强调 事务的独立性

持久性(durability):事务一旦结束,数据应持久到数据库中,强调了 事务的可持久性

不同的隔离性引发的安全性问题

脏读:一个事务读取到了另一个事务的 未提交
的数据。这些数据可能会被提交,也可能不会被提交。提交了还好,如果未提交,就读取到了脏数据。

不可重复读:一个事务读取到了另一个事务 已经提交了的 update 数据
,导致多次查询结果不一致.。侧重点是更新,重复读取数据时,若有其它事务对这些数据进行了更新操作,会导致查询到数据不一致。

幻读:一个事务读取到了另一个事务 已经提交了的 insert 数据
,导致多次查询结果不一致。侧重点是插入,重复读取数据时,若有其它事务进行了插入操作,会导致查询到数据条数不一致。

事务的隔离级别

读取未提交(read uncommited):脏读,不可重复读,幻读都有可能发生。

读取已提交(read commited):避免了脏读,但是不可重复读和幻读都有可能发生。

可重复读(repeatable read):避免了脏读和不可重复读,但是幻读有可能发生。

串行化(serializable):避免了以上问题,但效率较低。

Mysql 默认的事务隔离级别:可重复读 Oracle 默认的事务隔离级别:读取已提交


spring中定义的事务传播行为

* 保证同一个事务中

PROPAGATION_REQUIRED:支持当前事务,如果不存在,就新建一个事务(默认属性)

PROPAGATION_SUPPORTS:支持当前事务,如果不存在,就不使用事务

PROPAGATION_MANDATORY:支持当前事务,如果不存在,抛出异常

* 保证没有在同一个事务中

PROPAGATION_REQUIRES_NEW:如果有事务存在,挂起当前事务,创建一个新的事务

PROPAGATION_NOT_SUPPORTEDd:以非事务方式运行,如果有事务存在,挂起当前事务

PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常

PROPAGATION_REQUIRED_NESTED: 如果当前事务存在,则嵌套事务执行

事务传播行为的解读

ServiceA { 
    static void methodA() { 
        ServiceB.methodB(); 
    } 
} 

ServiceB { 
    static void methodB() {
    } 
}

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()也要回滚。

PROPAGATION_SUPPORTS(如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行)

和平常不加事务的代码区别很小,只是多了一个在事务中就以事务的形式运行的条件。

PROPAGATION_MANDATORY(必须在一个事务中运行)

也就是说:

他只能被一个父事务调用。否则,抛出异常。

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()事务仍然可能提交。

PROPAGATION_NOT_SUPPORTED(以非事务方式运行,如果有事务存在,挂起当前事务)

比如说:

ServiceA.methodA()的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB()的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB()时,ServiceA.methodA()的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA()的事务。

PROPAGATION_NEVER(以非事务方式运行,如果有事务存在,抛出异常)

假设ServiceA.methodA()的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB()的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB()就要抛出异常了。

PROPAGATION_NESTED(如果当前事务存在,则嵌套事务执行)

ServiceA { 
    void methodA() { 
        try { 
            //savepoint 
            ServiceB.methodB(); //PROPAGATION_NESTED 级别 
        } catch (SomeException) { 
            // 执行其他业务, 如 
            ServiceC.methodC(); 
        } 
    } 
}

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。

也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。也就是说ServiceB.methodB()失败回滚,那么ServiceA.methodA()也会回滚到savepoint点上,ServiceA.methodA()可以选择另外一个分支,比如ServiceC.methodC(),继续执行,来尝试完成自己的事务。

原文 

http://www.cnblogs.com/skindream/p/12507324.html

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 事务特性,事务的隔离级别以及spring中定义的事务传播行为

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址