大风--Spring系列之事务(四)
本篇文章小宋主要讲一下Spring事务的相关知识和注解。
文章目录
Spring事务
Spring的两种管理事务方式
-
编程式事务,在代码中硬编码。(不推荐使用)
-
声明式事务,在配置文件中配置(推荐使用)
声明式事务又分为两种:
- 基于XML的声明式事务
- 基于注解的声明式事务
Spring 并发事务中存在的3种问题
这里我顺便把脏读,不可重复读和幻读的3种情况分析一下。
脏读:
脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
eg.事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了
被事务T1修改后的数据,之后事务T1因为某种原因回滚(Rollback)了,
那么事务T2读取的数据就是脏的(无效的)。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED(读已提交)以上。
不可重复读:
不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。
eg.事务T1会读取两次数据,在第一次读取某一条数据后,
事务T2修改了该数据并提交了事务,T1此时再次读取该数据,
两次读取便得到了不同的结果。
解决办法:把数据库的事务隔离级别调整到REPEATABLE_READ(可重复读)
幻读:
幻读也是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。
eg. 系统事务A将数据库中所有数据都删除的时候,
但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,
就好像发生了幻觉一样。这就叫幻读。
解决办法:把数据库的事务隔离级别调整到SERIALIZABLE(序列化执行),或者数据库使用者自己进行加锁来保证。
Spring 事务中的隔离级别有哪几种?
Spring在TransactionDefinition 接口中定义了五个表示隔离级别的常量:
-
1.TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
-
2.TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
-
3.TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
-
4.TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-
5.TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Mysql的REPEATABLE_READ隔离级别如何解决幻读
上面我们提到了幻读这个问题,但是mysql是可重复读的级别,它又是怎么去解决幻读这个问题的呢?让我们继续往下看。
MySql从之前的版本到现在一共有两种引擎MyIsam和Innodb。
这里我们先说一下Innodb是怎么解决幻读问题的,其它的知识会在Mysql专栏中讲解。
首先了解一下Innodb的锁机制,Innodb有三种行锁的算法。
- Record Lock:单个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况
- Next-Key Lock:前两个锁的和(record+gap),锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题
Next-Key Lock(用来解决幻读)
带排他锁操作(除了INSERT/UPDATE/DELETE这种,
还包括SELECT FOR UPDATE等),它们默认都在操作的记录上加了Next-Key Lock。
只有使用了这里的操作后才会在相应的记录周围和记录本上加锁,
即Record Lock+ Gap Lock,所以会导致冲突的事务阻塞或超时失败。
其实隔离级别越高并发度越差,性能越差,虽然默认的是RR,但是如果业务不需要严格的没有幻读现象,是可以降低为RC的或修改innodb_locks_unsafe_for_binlog为1。
注意有的时候会进行优化,并退化为只加Record Lock,不加Gap Lock,如相关字段为主键的时候。
Mysql的REPEATABLE_READ隔离级别如何解决可重复读
MVCC(Multi-Version Concurrency Control多版本并发控制–用来解决可重复读)
如果是简单的SELECT * FROM table1 WHERE,这种语句为什么读不到隔壁事务
的提交数据的原因是,InnoDb使用了MVCC机制,为了提高并发,
提供了这个非锁定读,即不需要等待访问行上的锁释放,读取行的一个快照即可。
但是,它也不会阻止隔壁事务去插入新的数据,因为它并未有加锁操作,但当前事务读不到而已(其实想读也可以读到,这里就不讲方法了会在mysql专栏专门开文章讲解)。
Spring 的7种事务传播机制
支持当前事务的情况:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况: - TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,
如果当前存在事务,则抛出异常。
其他情况: - TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,
则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,
则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
@Transactional(rollbackFor = Exception.class)注解
大家都知道,Exception分为运行时异常RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
当@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该注解来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。
讲到这里本章对Spring的基础事务知识的讲解也就结束了,如果想了解更多知识可以在对应的专栏中看系列文章,谢谢大家的观看,希望能给各位同学带来帮助。如果觉得博主写的还可以的,可以点赞收藏。 😉

浙公网安备 33010602011771号