首先,事物的使用可以使我们极大的避免出现逻辑处理失败导致的脏数据等问题。
事务是逻辑处理原子性的保证手段,他有最重要的两个特性,分别是事务的传播级别和数据隔离级别。
1、传播级别定义的是事务的控制范围。
2、隔离级别定义的是事务在数据库读写方面的控制范围。
一、事务的传播级别:
(1)、 PROPAGATION_REQUIRED ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
(2)、PROPAGATION_SUPPORTS ,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
(3)、PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
(4)、PROPAGATION_REQUIRES_NEW ,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
【这里存在一个问题假如其中一个子事务回滚了,父事务是否回滚?当然是不会的,因为子事务是新建事务,父事务已经被挂起,两者不会受到影响。那么假如父事务回滚了,子事务是否回滚?依然是不会的,同样的道理。不过可以手动控制一旦子事务回滚,父事务也回滚。】
(5)、PROPAGATION_NOT_SUPPORTED ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
【这个级别可以帮助我们将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。】
(6)、PROPAGATION_NEVER ,此事务更加严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!
(7)、PROPAGATION_NESTED ,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
【嵌套是子事务套在父事务中执行,子事务是父事务的一部分,(例如循环内的循环体)在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,子事务执行结束,父事务继续执行。重点就在于那个save point。如果子事务回滚,父事务回滚到save point,之前的操作不会回滚,如果父事务回滚,则子事务也跟着回滚,事务的提交可想而知应该是子事务先提交,然后父事务再提交】
二、事务的隔离级别:
(1)、Serializable :最严格级别,事务串行执行,资源消耗最大;
(2)、REPEATABLE READ :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
(3)、READ COMMITTED :大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
(4)、Read Uncommitted :保证了读取过程中不会读取到非法数据。
事务隔离级别是否会脏读、不重复读和幻读对照:
Dirty reads non-repeatable reads phantom reads
(1) NO NO NO
(2) NO NO YES
(3) NO YES YES
(4) YES YES YES
事务的属性:readonly和timeout
(1)设置事务为只读以提升性能。
(2)设置事务的超时时间,用于防止大事务的发生。
注意:不能为了减小事务而将检查性的内容放到事务之外!
在处理核心的业务检查逻辑时,一定不能放到事务之外,而且必须要作为分布式下的并发控制!
一旦在事务之外做检查,那么势必会造成事务A已经检查过的数据被事务B所修改,导致事务A徒劳无功而且出现并发问题,直接导致业务控制失败。因此在分布式的高并发环境下,核心业务逻辑的检查,要采用加锁机制。
好的实践方式是使用编程式事务而非生命式
将DAO保持针对一张表的最基本操作,然后业务逻辑的处理放入manager和service中进行,同时使用编程式事务更精确的控制事务范围。
特别注意,对于事务内部一些可能抛出异常的情况,捕获要谨慎,不能随便的catch Exception 导致事务的异常被吃掉而不能正常回滚。在高并发的分布式系统中,本地事务的处理要极为小心,比如ws超时等问题是否要回滚当前事务或者触发重试机制,都要考虑特定的业务场景来决定。