oracle事物的特征,oracle事物隔离级别跟spring的事务传播特性

oracle事物隔离级别和spring的事务传播特性

oracle数据库的事务隔离级别:

在实际开发过程中,特别是在并发高、更新数据量大、关系表比较多的情况下,经常会遇到关于事务的问题。

首先,要了解的是什么是数据库的隔离级别。在一个典型的应用中,并发是不可避免的,多个事务并发运行,操作同一个数据来完成任务。

并发可能会导致以下问题:

脏读(Dirty read):脏读发生在一个事务读取了被另一个事务改写但还未提交的数据时。如果这些改变在稍后被回滚,

那么之前的事务读取的到数据就是无效的。

也就是在一个事务中的更新语句加了锁,在未提交之前,其他事务无法读取该数据。举个例子:

两个事务:取款和转账。A是取款事务,B是转账事务。假设账户有1000元,A事务取100元,B事务向账户汇入100元,当A事务减掉100元后,

但还未提交,此时账户还有900元,在这时候B账户查到账户有900元,加上100元,等于1000元,之后A事务由于某种原因撤销事务了,

钱没取出来,然后B事务提交了,此时账户只有1000元,但是用户没有取得100元,相当于用户损失100元。

不可重复读(Nonrepeatable read):不可重复读发生在一个事务执行相同的查询两次或两次以上,但每一次的查询结果不同时。

这通常是由于另一个并发的事务在两次查询之间更新了数据。

举个例子:

比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,

total竟然就变成200了,造成事务A数据混乱。

也就是数据在被selelct的时候没有加上锁,导致其他事务可以更改它的数据。第二次再读的时候,数据已经被改变了。

幻读(Phantom read):幻读是一个事务读取几行记录后,另一个事务插入了一些记录,幻读就发生了。

这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。

但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),

但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。

比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,

由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

不可重复读和幻读的区别在于解决手段,以下是一个不严谨的解释:

对于select * from t where id='xxx',如果其它事务更改了id为xxx的记录,就会导致这一条语句两次读取的内容不一致,要防止不可重复读,只需锁定id为xxx的行就可以

而select * from t where age > 50,如果其它事务更改了别的数据,使原本age不大于50的变成了age>50(同理还有insert一条age>50的记录),那就会使得第二次读取时的结果集里记录多出一部分(或者消失了一部分),就好像产生幻觉了,要防止幻读就不是锁定age>50的行能够解决的,而需要锁定查询涉及到的全集

所以这两个概念不是凭空制订的,他们代表着两个不同的锁定范围,代表这不同的两个数据一致性和性能的平衡,这也正是隔离级别的核心意义

查了下资料并加上自己的理解:

Isolation Level(事务隔离等级): 总共有四个隔离级别。

1、Serializable:最严格的级别,事务串行执行,资源消耗最大;

2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。

3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。

4、Read Uncommitted:保证了读取过程中不会读取到非法数据。

Dirty reads          non-repeatable reads            phantom reads

Serializable                     不会                   不会                           不会

REPEATABLE READ           不会                   不会                            会

READ COMMITTED            不会                    会                             会

Read Uncommitted            会                     会                             会

spring的事务隔离级别

ISOLATION_DEFAULT:使用数据库默认的隔离级别。 oracle数据库的默认隔离级别是:read_commited;

ISOLATION_READ_UNCOMMITTED:允许读取改变了的还未提交的数据,可能导致脏读、不可重复读和幻读。

ISOLATION_READ COMMITTED:允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读。

ISOLATION_REPEATABLE_READ:对相同字段的多次读取结果一致,可导致幻读。

ISOLATION_SERIALIZABLE:完全服从ACID的原则,确保不发生脏读、不可重复读和幻读。

可以根据自己的系统对数据的要求采取适应的隔离级别,因为隔离牵涉到锁定数据库中的记录,对数据正性要求越严格,并发的性能也越差。

spring的事务传播行为

spring事务的传播行为说的是当一个方法调用另一个方法时,事务该如何操作。

PROPAGATION_MANDATORY:该方法必须运行在一个事务中。如果当前事务不存在则抛出异常。

PROPAGATION_NESTED:如果当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交和回滚。如果当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持参差不齐,使用时需注意。

PROPAGATION_NEVER:当前方法不应该运行在一个事务中。如果当前存在一个事务,则抛出异常。

PROPAGATION_NOT_SUPPORTED:当前方法不应该运行在一个事务中。如果一个事务正在运行,它将在该方法的运行期间挂起。

PROPAGATION_REQUIRED:该方法必须运行在一个事务中。如果一个事务正在运行,该方法将运行在这个事务中。否则,就开始一个新的事务。

PROPAGATION_REQUIRES_NEW:该方法必须运行在自己的事务中。它将启动一个新的事务。如果一个现有的事务正在运行,将在这个方法的运行期间挂起。

PROPAGATION_SUPPORTS:当前方法不需要事务处理环境,但如果一个事务已经在运行的话,这个方法也可以在这个事务里运行。

在现实的应用中,由于业务上的需要,要求不同表中的数据保持一致,即要求不同表中的数据同时更新或者出错时同时回滚,事务的本质其实就是为了解决这样的问题。

举例子解释spring的事务传播行为:

********************sample***********************

ServiceA {

/**

* 事务属性配置为 PROPAGATION_REQUIRED

*/

void methodA() {

ServiceB.methodB();

}

}

ServiceB {

/**

* 事务属性配置为 PROPAGATION_REQUIRED

*/

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 {

/**

* 事务属性配置为 PROPAGATION_REQUIRED

*/

void methodA() {

try {

//savepoint

ServiceB.methodB();    //PROPAGATION_NESTED 级别

} catch (SomeException) {

// 执行其他业务, 如 ServiceC.methodC();

}

}

}

********************************************

也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如

ServiceC.methodC,继续执行,来尝试完成自己的事务。

但是这个事务并没有在EJB标准中定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值