从Spring事务的隔离级别说起
Posted 春哥叨叨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Spring事务的隔离级别说起相关的知识,希望对你有一定的参考价值。
隔离级别(isolation)定义了事务并发的隔离程度。
数据隔离级别分为不同的四种:
Serializable :最严格的级别,事务串行执行,资源消耗最大;
REPEATABLE READ :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
READ COMMITTED :大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
Read Uncommitted :保证了读取过程中不会读取到非法数据。
上面的解释其实每个定义都有一些拗口,其中涉及到几个术语:脏读、不可重复读、幻读。
这里解释一下:
脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
不可重复读的重点是修改:
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了。
读的重点在于新增或者删除:
同样的条件, 第1次和第2次读出来的记录数不一样。
从控制的角度来看, 两者的区别就比较大:
对于前者, 只需要锁住满足条件的记录。
对于后者, 要锁住满足条件及其相近的记录。
一个对照关系表:Dirty reads non-repeatable reads phantom reads Serializable 不会 不会 不会 REPEATABLE READ 不会 不会 会 READ COMMITTED 不会 会 会 Read Uncommitted 会 会 会
所以最安全的,是Serializable,但是伴随而来也是高昂的性能开销。另外,事务常用的两个属性:readonly和timeout 一个是设置事务为只读以提升性能。另一个是设置事务的超时时间,一般用于防止大事务的发生。还是那句话,事务要尽可能的小!
最后引入一个问题:一个逻辑操作需要检查的条件有20条,能否为了减小事务而将检查性的内容放到事务之外呢?
很多系统都是在DAO的内部开始启动事务,然后进行操作,最后提交或者回滚。这其中涉及到代码设计的问题。小一些的系统可以采用这种方式来做,但是在一些比较大的系统, 逻辑较为复杂的系统中,势必会将过多的业务逻辑嵌入到DAO中,导致DAO的复用性下降。所以这不是一个好的实践。
来回答这个问题:能否为了缩小事务,而将一些业务逻辑检查放到事务外面?
答案是:对于核心的业务检查逻辑,不能放到事务之外,而且必须要作为分布式下的并发控制!
一旦在事务之外做检查,那么势必会造成事务A已经检查过的数据被事务B所修改,导致事务A徒劳无功而且出现并发问题,直接导致业务控制失败。
所以,在分布式的高并发环境下,对于核心业务逻辑的检查,要采用加锁机制。比如事务开启需要读取一条数据进行验证,然后逻辑操作中需要对这条数据进行修改,最后提交。这样的一个过程,如果读取并验证的代码放到事务之外,那么读取的数据极有可能已经被其他的事务修改,当前事务一旦提交,又会重新覆盖掉其他事务的数据,导致数据异常。所以在进入当前事务的时候,必须要将这条数据锁住,使用for update就是一个很好的在分布式环境下的控制手段。
一种好的实践方式是使用编程式事务而非生命式,尤其是在较为规模的项目中。对于事务的配置,在代码量非常大的情况下,将是一种折磨,而且人肉的方式,绝对不能避免这种问题。
将DAO保持针对一张表的最基本操作,然后业务逻辑的处理放入manager和service中进行,同时使用编程式事务更精确的控制事务范围。特别注意的,对于事务内部一些可能抛出异常的情况,捕获要谨慎,不能随便的catch Exception 导致事务的异常被吃掉而不能正常回滚。
你好,我是春哥叨叨,更多真实架构案例分享,等你很久了!
春哥叨叨
以上是关于从Spring事务的隔离级别说起的主要内容,如果未能解决你的问题,请参考以下文章