不考虑事务隔离产生的三大问题
1.脏读:
定义:一个事务处理过程里读取了另一个未提交的事务中的数据
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。
2.不可重复读:
定义:对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值(这是由于在查询间隔,被另一个事务修改(update)并提交了)
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
3.幻读:
定义:对于数据库中的某批数据,一个事务范围内多次查询却返回了不同的数据值(这是由于在查询间隔,被另一个事务插入(insert)并提交了)
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
注意:脏读是应该避免的(看到的是假数据),而不可重复读和幻读有时候则不是问题(看到的是会变化的真数据)
4种事务隔离级别
1.Read uncommitted 读未提交:
一个事务可以读取另一个未提交事务的数据(安全性低,不推荐)
造成问题:脏读、不可重复读、幻读
2.Read committed 读已提交:
一个事务要等另一个事务提交后才能读取数据
解决问题:脏读
造成问题:不可重复读 、幻读
3.Repeatable read 可重复读:
在开始读取数据(事务开启)时,不再允许修改操作
解决问题:脏读,不可重复读
造成问题:幻读
4.Serializable 可序列化:
事务串行化顺序执行(效率低,不推荐)
解决问题:脏读、不可重复读、幻读
注意点:
安全性越高,性能消耗越大
Oracle只支持 读已提交 和 可序列化,默认隔离级别为 读已提交
Mysql默认隔离级别为可重复读
7种事务传播行为
1.PROPAGATION_REQUIRED(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(propagation_supports,事务支持)
定义:当前方法不必但支持在一个事务中运行
做法:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3.PROPAGATION_MANDATORY(propagation_mandatory,事务强制)
定义:当前方法必须在一个事务中运行,如果没有事务,将抛出异常
4.PROPAGATION_REQUIRES_NEW(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(propagation_not_supported,事务不支持)
定义:该方法不支持在一个事务中运行
做法:如果有一个事务正在运行,他将在运行期被挂起,直到当前方法以非事务的状态执行完才恢复执行
例子:
ServiceA.methodA的事务级别是PROPAGATION_REQUIRED 。而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时。ServiceA.methodA的事务挂起。而他以非事务的状态执行完,再继续ServiceA.methodA的事务。
6.PROPAGATION_NEVER(propagation_never,事务拒绝)
定义:当前方法不应该在一个事务中运行,如果存在一个事务,则抛出异常
7.PROPAGATION_NESTED(propagation_nested,事务嵌套)
定义:当前方法必须运行在它自己的嵌套事务中
做法:外层有事务,则新建嵌套事务运行其中,外层无事务,则如同PROPAGATION_REQUIRED
注意:如果外层事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。
注意点:
在外层事务存在的情况下,PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED三者区别
PROPAGATION_REQUIRED:
加入已存在事务,即可视为,内层事务与外层事务同步,外影响内,内影响外
本质:一个事务
PROPAGATION_REQUIRES_NEW:
强制新建事务,即可视为,内层事务与外层事务独立,外不影响内,内不影响外
本质:两个事务,提交点独立
PROPAGATION_NESTED:
嵌套事务,即可视为,内层事务依赖于外层事务,外影响内,内不影响外
本质:两个事务,提交点统一
另外还需注意自调用(自己的类调用其他方法的过程)而使传播行为无效的问题