Spring事务基本知识点

事务的ACID

事务是区分文件存储系统与NoSql数据库重要特性之一,其存在的意义是为了保证即使在并发情况下也能正确的执行crud操作,怎样才算是正确的呢?这时提出了事务需要保证的四个特性ACID:

  • A:原子性(atomicity)
    • 事务中各项操作,要么全做要么全不做,任何一项操作的失败都都会导致整个事务的失败;
  • C:一致性(consistency)
    • 事务结束后系统状态是一致的;
  • I:隔离性(isolation)
    • 并发执行的事务彼此无法看到对方的中间状态;
  • D:持久性(durability)
    • 事务完成后所做的改动都会被持久化,即使发生灾难性的失败;

在高并发的情况下,要完全保证其ACID特性是非常困难的,除非把所有的事务串行化执行,但带来的负面的影响将是性能大打折扣。很多时候我们有些业务对事务的要求是不一样的,所以数据库中设计了四种隔离级别,供用户基于业务进行选择。

事务隔离级别

在高并发的场景下会出现的事务问题如下**Dirty Reads  脏读:**事务A正在对数据进行更新操作,但是更新还未提交,事务B这时也来操作这组数据,并且读取了事务A还未提交的数据,而事务A如果操作失败进行了回滚,事务B读取的就是错误数据,这样就造成了脏读。**Non-Repeatable Reads 不可重复读:**在同一事务中,多次读取同一数据返回的结果有所不同,换句话说,事务A两次查询中间读取到事务B已提交的更新数据;相反,“可重复读”在同一事务中多次读取数据时,能够保证所读取数据不能读取到另一事务已提交的更新数据。**Phantom Reads 幻读:**查询表中一条数据如果不存在就插入一条;并发的时候却发现数据库中保存了多条先相同的数据,(因为多个线程同时查询数据库时都发现数据不存在,则都进行插入操作)这就是幻读。

隔离级别脏读(Dirty Reads)不可重复读 (NonRepeatable Reads)幻读(Phantom Reads)
未提交读(isolation = Isolation.READ_UNCOMMITTED)( 基本不使用)可能可能可能
已提交读 (isolation = Isolation.READ_COMMITTED)不可能可能可能
可重复读 (isolation = Isolation.REPEATABLE_READ)不可能不可能可能
可串行化 (isolation = Isolation.SERIALIZABLE)不可能不可能不可能

不同数据库中的默认隔离级别有所不同:

  1. MYSQL: 默认为REPEATABLE_READ级别
  2. SQLSERVER: 默认为READ_COMMITTED
  3. ORACLE:默认为 READ_COMMITTED

mysql 查询事务隔离级别:show variables like '%tx_isolation%'

Spring事务传播级别

类别事务传播类型说明
支持当前事务PROPAGATION_REQUIRED(必须的)如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到这个事务中,这是最常见的选择,Spring默认使用传播级别。
PROPAGATION_SUPPORTS(支持)支持当前事务,如果当前没有事务,就以非事务方法执行
PROPAGATION_MANDATORY(强制)使用当前的事务,如果当前没有事务,就抛出异常。
不支持当前事务PROPAGATION_REQUIRES_NEW(隔离)新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED(不支持)以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NERVER(强制非事务)以非事务方式执行,如果当前存在事务,则抛出异常
嵌套事务PROPAGATION_NESTED(嵌套事务)如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作;PROPAGATION_NESTED 是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.

Spring事务使用过程中容易混淆的知识点

Spring中使用事务原则 一、假如调用方法是A,被调用方法是B;只要方法B抛出了异常,那么方法A则也会回滚,在执行完方法B之后方法A抛异常,则需要看方法B上的事务传播行为。 二、Spring中本类里面事务方法A调用事务方法B,那么事务方法B上的事务注解将会失效,方法B将会作为方法A的一部分,加入到方法A的事务中。

  1. Spring中使用PROPAGATION_REQUIRED作为默认传播行为
  2. Spring事务传播行为中PROPAGATION_REQUIREDPROPAGATION_NESTED是比较像的,对于下面示例代码,funB()上使用PROPAGATION_REQUIREDPROPAGATION_NESTED都会引起funB回滚
serviceA{
    @Transactional
    funcA(){
        serviceB.funcB();
        int i = 1/0; // 模拟异常报错
    }
}
serviceB{
    @Transactional
    funcB(){
        // 执行insert或update操作
    }
}
  1. 在上面的示例代码中,如果funB使用PROPAGATION_REQUIRES_NEW, 则funcB将会启动一个独立事务,此时funcB提交成功后,funA的异常不会引起funB的回滚。