事务的传播行为与隔离级别

事务的传播行为与隔离级别

前一段日子有人问我关于事务的传播行为,当时脑袋有点蒙,居然回答到了事务隔离性上,不免有些汗颜。最近利用闲暇之余看了看这方面的博客,但是国内相关博客水平参差不齐,为了方便以后自己阅读,带着自己的理解在此总结一下。

首先介绍一下事务:个人理解如果某个方法在事务中运行,那么当我们执行该方法操作数据库时,对数据库的所有操作要么都成功,要么都失败。对于事务传播行为的引入也是由于我们经常在项目中使用某个方法操作数据库的同时,还经常调用另一个方法去操作数据库,如果一开始的某个方法存在事务环境,那么这个事务也会影响被调用方法的事务环境,这就产生了事务的传播行为。

事务的传播行为

为了方便说明事务的传播行为,不妨先假设一个实际生产环境,不妨有两个service层方法,分别为serviceMethodA()与serviceMethodB()方法,其中serviceMethodA中会调用serviceMethodB这个方法。

  • Propagation_REQUIRED
    • 表示当前方法必须要运行在事务中,如果当前环境存在事务,该方法将会在该事务中运行,如果当前环境不存在事务,则会启动一个新事务
    • 假设serviceMethodB方法的事务级别定义为Propagation_REQUIRED
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,将不会再次开启事务。当serviceMethodA或者serviceMethodB两者有一个出现异常的时候,两方法均会被回滚
      • 如果serviceMethodA没有开启事务,那么在执行serviceMethodB的时候,将会开启事务执行。当serviceMethodB出现异常的时候,serviceMethodB将会被回滚,如果在serviceMethodA中非serviceMethodB出现异常时,不会发生回滚
  • Propagation_SUPPORTS
    • 表示当前方法不需要事务环境,但是如果当前环境存在事务的话,那么该方法会在这个事务中运行
    • 假设serviceMethodB方法的事务级别定义为Propagation_SUPPORTS
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,将加入到当前事务中。当serviceMethodA或者serviceMethodB两者有一个出现异常的时候,两方法均会被回滚
      • 如果serviceMethodA没有开启事务,那么在执行serviceMethodB的时候,将不会开启事务(正常执行)。无论当serviceMethodA或者serviceMethodB两者有一个出现异常的时候,两方法均不会被回滚
  • Propagation_MANDATORY
    • 表示当前方法必须运行在事务环境中,如果当前不存在事务,则会抛出异常
    • 假设serviceMethodB方法的事务级别定义为Propagation_MANDATORY
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,将加入到当前事务中。当serviceMethodA或者serviceMethodB两者有一个出现异常的时候,两方法均会被回滚
      • 如果serviceMethodA没有开启事务,那么在执行serviceMethodB的时候,将直接会抛出异常
  • Propagation_REQUIRED_NEW
    • 表示当前方法必须运行在自己的事务中,如果当前环境存在事务,则会挂起当前事务,然后新启动一个事务
    • 假设serviceMethodB方法的事务级别定义为Propagation_REQUIRED_NEW
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,会将serviceMethodA的事务挂起,serviceMethodB会开启一个新的事务。当serviceMethodB执行完毕之后,在将serviceMethodA之前的事务重新“运行”。由于serviceMethodA与serviceMethodB运行在两个事务中,所以当serviceMethodB执行过程中出现异常,仅仅只有serviceMethodB会回滚,当serviceMethodA执行非serviceMethodA时出现异常,仅仅serviceMethodA回滚
      • 如果serviceMethodA没有开启事务,那么在执行serviceMethodB的时候,会新启动一个事务,当serviceMethodB出现异常,会回滚serviceMethodB。由于serviceMethodA没有开启事务,所以当serviceMethodA执行非serviceMethodB时出现异常,serviceMethodA不会被回滚
  • Propagation_NOT_SUPPORTED
    • 表示当前事务不应该执行在具有事务的环境中,如果当前环境存在事务,则将事务挂起
    • 假设serviceMethodB方法的事务级别定义为Propagation_NOT_SUPPORTED
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,会将serviceMethodA的事务挂起,然后运行serviceMethodB方法,等serviceMethodB方法执行完,在重新“运行”serviceMethodA的事务。如果serviceMethodB出先异常,则serviceMethodA与serviceMethodB均不会回滚;当serviceMethodA执行非serviceMethodB时出现异常,serviceMethodA会被回滚,但serviceMethodB不会。
      • 如果serviceMethodA没有开启了事务,那么在调用serviceMethodB的时候,serviceMethodB方法直接运行。无论是serviceMethodA或serviceMethodB出现异常时,均不会被回滚
  • Propagation_NEVER
    • 表示当前方法不应该运行在事务环境中,如果当前环境存在事务,则抛出异常
    • 假设serviceMethodB方法的事务级别定义为Propagation_NEVER
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,则之间抛出异常
      • 如果serviceMethodA没有开启了事务,那么在调用serviceMethodB的时候,serviceMethodB方法直接运行。无论是serviceMethodA或serviceMethodB出现异常时,均不会被回滚
  • Propagation_NESTED
    • 表示如果当前环境存在事务,则该方法会开启一个事务嵌套在当前事务中,如果当前环境没有事务,则与Propagation_REQUIRED行为一致
    • 假设serviceMethodB方法的事务级别定义为Propagation_NESTED
      • 如果serviceMethodA已经开启了事务,那么在调用serviceMethodB的时候,serviceMethodB会开启事务,当serviceMethodB出现异常时,serviceMethodB会回滚,serviceMethodA不会被回滚,但是当serviceMethodA执行非serviceMethodB时出现异常,则serviceMethodA与serviceMethodB均会被回滚
      • 如果serviceMethodA没有开启了事务,那么在调用serviceMethodB的时候,serviceMethodB会开启事务。当serviceMethodB出现异常时,serviceMethodB会回滚。

默认情况下当发生RuntimeException的情况下,事务才会滚回。

事务的隔离级别

 在说起事务隔离级别之前,有必要先介绍一下脏读、不可重复读、幻读的概念

  • 脏读:事务A未提交(但还缓存)的数据被事务B读走,如果事务A失败而导致回滚,那么此时事务B所读到的数据就是错误的
  • 不可重复读:事务A在执行过程中有两次或两个以上读取某个值V,在事务A第一次读取V之后,事务B将V的值改变了,当事务A再次读取V的时候,会出现前后两次读取值不一致
  • 幻读:幻读与不可重复读相似,但不可重复读出现的原因是事务B改变了事务A所读取的结果集,但幻读是由于事务B改变了事务A的条件集合,例如事务A要读取select age from table where name="zhang",对于不可重复读,是由于事务B改变了name等与zhang的数据中的age值,将原来的age(不妨假设为18)改为了28,这样在事务A再次读取时age值发生了变化。但是幻读是由于事务B将name等与zhang的值改成了li,随后将原来的chen改为了zhang,这样事务A再次读取zhang时就会出现结果不同的情况(因为此时的zhang已经不是原来的zhang了,而是chen)

隔离级别

  • Serializable:最严格的级别,事务串行运行,资源消耗大,能够完全避免脏读,不可重复度,幻读
  • Repeatable Read:保证了一个事务不会修改已经由另一个事务读取但未提交或未回滚的数据,避免了脏读和不可重复的情况
  • Read Commit:大多数主流的数据库默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了脏读
  • Read UnCommit:仅仅只能保证读取过程中不会读取到非法数据