事务总结

 有时候一个业务需要多次操作数据库,比如转账:

事务总结

如果reduce()执行成功,add执行失败,那钱是转出去了,但对方并没有收到,钱转丢了。

使用事务可解决此问题。

 事务:逻辑上的一组操作,要么全部成功,要么全部失败。


事务的4个特性

  • 原子性:最小单元,不可再分割
  • 一致性:事务执行前后,数据的完整性保持一致。比如说A转账给B,转账前两人账户加起来一共有5W元,转账后两人账户加起来也应该是5W元。
  • 隔离性:一个事务的执行不应受到其他事务的干扰
  • 持久性:一旦事务结束(包括成功、失败回滚),数据就持久化到数据库

如果没做到隔离性,可能会引发读写问题

读问题:

  • 脏读
  • 不可重复读
  • 虚读(幻读)

写问题:

  • 丢失更新

脏读     读取另一个事务未提交的数据

原本数据库中的数据是正确的、干净的,

B事务更改了数据库中的数据,

A事务读取数据库中的数据(被事务B修改后的数据),

事务B发生错误,将数据库中的数据回滚成原本干净的数据,A读取到的数据变成了无效数据、脏数据。

但事务A什么都不知道,使用之前读取到的数据(脏数据)做一些操作。

示例    B取款的同时,A转账给B

事务总结

虽然2个事务是并发执行的,但实际上,执行具体代码时仍有先后顺序。


不可重复读     前后多次读取,读取到的数据内容不一致(期间已被其它事务修改)

事务A比较大,前后需要多次从数据库读取同一段数据。

开始事务

前一次从数据库读取数据,赋给一些变量来使用,

使用这些变量来做一些操作,

随着代码的执行,这些变量逐渐被销毁(方法调用结束,局部变量被销毁;对象长时间闲置,被gc回收等),

之后需要再次使用这些数据,因为变量已被销毁,只有重新从数据库读取数据(执行的仍是之前的sql语句)来使用,

但这2次读取期间,有其它的事务修改了数据库中的这些数据的内容,

2次读取到的数据不一致(不是重复的),称为不可重复读。

但事务A不知道前后的数据不一致,仍然使用第二次读取的数据做一些操作。

提交事务。

第一次读取的数据已经无效了,使用这些数据做的操作无效,即前面部分做的操作与后面部分做的操作对不上。


虚读(幻读)  前后多次读取,读取到的记录数不一致(期间已被其它事务修改)

虚读和不可重复读差不多,都是前后读取相同的内容(执行一样的sql查询),

不可重复读是记录数没变化、但记录的内容被其它事务修改了(update  xxx_tb  set  xxx=xxx),

虚读是读取到的记录数变了(其它事务使用insert、delete修改了数据库中读取的部分,导致记录总数不一致)。


可通过设置事务的隔离级别,来解决读问题。

事务的4种隔离级别

  • Read uncommitted:未提交读,解决不了任何读问题,安全性最低,但事务执行效率最高
  • Read committed:已提交读,解决了脏读,但不可重复读、虚读有可能发生
  • Repeatable read:重复度,解决了脏读、不可重复读,但虚读有可能发生
  • Serializable:不允许事务并发,安全性最高,但事务执行效率最低

一般是折中使用第2、3项,mysql用Repeatable read,oracle用Read committed。


事务的传播行为

 事务总结

事务管理是添加在业务层的,给Service中的方法添加事务管理。

一般情况下:

  • 在Service中调用某个Dao的1个或多个方法,比如上图的Service1
  • 在Service中调用1个或多个Dao的方法,比如上图的Service2

就可以完成业务。

但有时候业务特别复杂,需要调用业务层的其它方法:

事务总结

包括调用其它Service的方法、本Service的其它的方法。

调用的方法可能添加了事务,对于Service.x()带过来的事务(被调方法传播过来的事务),该如何处理?

事务的传播行为就是:在业务层的方法相互调用时,规定主调方法定义自身的事务管理(是否使用被调方法的事务)。


  

Spring中的7种事务传播行为

以下面的调用为例:

事务总结

大致可分为3类:(红色标示的项是常见的,只需记住红色标示的项)

(1)保证多个操作在同一个事务中

  • PROPAGATION_REQUIRED     默认值,一般都是使用这个。

如果被调方法有事务,主调方法就使用被调方法的事务;如果被调方法没有事务,会创建一个新事务,管理主调方法。

会把主调方法的其它代码包含进来,一起管理。被调方法指的是业务层的其它其它方法Service1.x(),主调方法指的是z(),包括Service1.x() + Dao2.c() 。

required   要求、需要、必需的,对于主调方法,事务是必需的

  • PROPAGATION_SUPPORTS   

如果被调方法有事务,主调方法就使用被调方法的事务;如果被调方法没有事务,主调方法就不使用事务。

supports,有就支持,没有就算了(都不使用事务)。

  • PROPAGATION_MANDATORY

如果被调方法有事务,主调方法就使用被调方法的事务;如果被调方法没有事务,则抛出异常。

(2)保证多个操作不在同一个事务中

  • PROPAGATION_REQUIRES_NEW   

requires_new   对于主调方法,事务是必需的,且事务要是新建的(只管里主调方法的其它部分,被调方法的事务管理维持原状)。

会新建一个事务,管理主调方法(的其它部分),被调方法的事务管理维持原状。

  • PROPAGATION_NOT_SUPPORTED

不管被调方法有没有事务,主调方法都不使用事务(其它部分不使用,被调方法的事务管理维持原状)。

not_supported,主调方法不支持事务。

  • PROPAGATION_NEVER

如果被调方法有事务,直接抛出异常。

never,决不能有事务。

(3)嵌套式事务

  • PROPAGATION_NESTED

在主调方法执行前、被调方法执行的前后设置还原点,失败时可以回滚到某个还原点。

执行主调方法之前先设置一个还原点,执行主调方法的其它代码(如果有)。

如果执行失败,回滚到之前的还原点;如果执行成功,再设置一个还原点,执行被调方法(如果被调方法有事务,会执行被调方法的事务)。

如果被调方法执行失败,回滚到之前的某个还原点(可设置),如果被调方法执行成功,设置一个还原点,执行后续的其它代码。

如果执行失败,回滚到之前的某个还原点(可设置)。

相关推荐