Spring事务的隔离级别
在数据库操作中,多个事务并发执行时可能会出现诸如脏读、不可重复读、幻读等问题,事务隔离级别就是用来解决这些问题的,Spring 支持的事务隔离级别和数据库的事务隔离级别相对应,主要有以下几种:
1. ISOLATION_DEFAULT
(默认隔离级别)
- 含义:使用底层数据库默认的事务隔离级别。不同数据库默认隔离级别不同,例如 MySQL 的默认隔离级别是
REPEATABLE_READ
,Oracle 的默认隔离级别是READ_COMMITTED
。 - 特点:这种方式会根据具体使用的数据库来决定事务隔离的效果,开发人员无需手动指定具体隔离级别,方便快捷,但缺乏一定的可控性。
2. ISOLATION_READ_UNCOMMITTED
(读未提交)
- 含义:一个事务可以读取另一个未提交事务的数据。这是隔离级别最低的情况,几乎不做任何隔离控制。
- 特点:存在脏读(一个事务读取到另一个事务未提交的数据)、不可重复读(在一个事务内,多次读取同一数据,结果却不同)、幻读(在一个事务内,多次执行相同的查询,结果集却不同)等问题 。但这种隔离级别下,事务之间的并发性能最高,因为几乎没有加锁等操作来限制并发。
3. ISOLATION_READ_COMMITTED
(读已提交)
- 含义:一个事务只能读取另一个已经提交事务的数据。
- 特点:可以避免脏读问题,但仍然可能出现不可重复读和幻读问题。相比
ISOLATION_READ_UNCOMMITTED
,它增加了一定的隔离性,在大多数数据库中,这是一种比较常用的隔离级别,在一定程度上平衡了并发性能和数据一致性。
4. ISOLATION_REPEATABLE_READ
(可重复读)
- 含义:在一个事务内,多次读取同一数据时,读取到的结果是一致的,即使在这个事务执行期间,其他事务对该数据进行了修改并提交。
- 特点:可以避免脏读和不可重复读问题,但在某些情况下(如插入操作),可能还是会出现幻读问题。MySQL 的默认隔离级别就是可重复读,并且通过 MVCC(多版本并发控制)等机制,在很大程度上避免了幻读问题,是一种比较可靠的隔离级别 。
5. ISOLATION_SERIALIZABLE
(可串行化)
- 含义:最高级别的隔离级别,事务串行执行,一个事务执行完之后,另一个事务才开始执行。
- 特点:可以完全避免脏读、不可重复读和幻读问题,保证了数据的绝对一致性。但由于事务只能串行执行,并发性能最差,会极大地降低系统的并发处理能力,一般在对数据一致性要求极高且并发量不大的场景下才会使用。
Spring事务的传播行为
事务传播行为是指当一个事务方法被另一个事务方法调用时,该如何传播事务,Spring 定义了 7 种事务传播行为:
1. PROPAGATION_REQUIRED
(required,默认传播行为)
- 含义:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- 示例:方法 A 没有事务,方法 B 被方法 A 调用,且方法 B 的事务传播行为是
PROPAGATION_REQUIRED
,那么方法 B 会创建一个新事务;若方法 A 本身存在事务,方法 B 就加入到方法 A 的事务中,两者共同构成一个事务,要么都成功提交,要么都回滚。
2. PROPAGATION_SUPPORTS
(supports)
- 含义:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。
- 示例:方法 A 没有事务,方法 B 被方法 A 调用,方法 B 以非事务方式执行;若方法 A 存在事务,方法 B 就加入到方法 A 的事务中。
3. PROPAGATION_MANDATORY
(mandatory)
- 含义:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- 示例:方法 B 要求必须在一个事务环境下执行,当被方法 A 调用时,如果方法 A 没有事务,调用方法 B 就会抛出异常,只有方法 A 存在事务时,方法 B 才会加入到该事务中。
4. PROPAGATION_REQUIRES_NEW
(requires_new)
- 含义:创建一个新的事务,如果当前存在事务,则把当前事务挂起,等新事务执行完毕后,再恢复当前事务。
- 示例:方法 A 有事务,方法 B 的事务传播行为是
PROPAGATION_REQUIRES_NEW
,当方法 A 调用方法 B 时,方法 B 会创建一个新事务独立执行,方法 A 的事务会被挂起,方法 B 执行完成后,方法 A 的事务才继续执行,两个事务相互独立,各自有自己的提交和回滚操作。
5. PROPAGATION_NOT_SUPPORTED
(not_supported)
- 含义:以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
- 示例:方法 A 有事务,方法 B 的事务传播行为是
PROPAGATION_NOT_SUPPORTED
,当方法 A 调用方法 B 时,方法 B 会以非事务方式执行,方法 A 的事务被挂起,等方法 B 执行完毕后,方法 A 的事务再继续执行。
6. PROPAGATION_NEVER
(never)
- 含义:以非事务方式执行,如果当前存在事务,则抛出异常。
- 示例:方法 B 不允许在事务环境下执行,当被方法 A 调用时,如果方法 A 存在事务,调用方法 B 就会抛出异常。
7. PROPAGATION_NESTED
(nested)
- 含义:如果当前存在事务,则创建一个事务嵌套在当前事务中执行;如果当前没有事务,则与
PROPAGATION_REQUIRED
一样,创建一个新事务。嵌套事务是一个独立的事务单元,可以单独回滚,但是当外层事务回滚时,内层事务也会回滚。 - 示例:方法 A 有事务,方法 B 的事务传播行为是
PROPAGATION_NESTED
,当方法 A 调用方法 B 时,方法 B 会创建一个嵌套事务,方法 B 可以单独提交或回滚,不影响方法 A 的事务,但是如果方法 A 回滚,方法 B 也会跟着回滚。
Spring事务传播行为的作用
- 满足复杂业务场景需求:在实际的业务开发中,存在各种复杂的调用关系和事务需求。例如,在一个电商系统中,订单创建方法可能会调用库存扣减方法、支付方法等,不同的方法对事务的要求不同。通过事务传播行为,可以灵活地配置每个方法的事务处理方式,确保业务逻辑的正确性和数据的一致性 。
- 提高系统的并发性能和资源利用率:合理地使用事务传播行为,可以避免不必要的事务开销,提高系统的并发性能。比如,对于一些只读操作的方法,使用
PROPAGATION_SUPPORTS
或者以非事务方式执行,可以减少事务锁的竞争,提高系统的并发处理能力。 - 增强代码的可维护性和可扩展性:事务传播行为将事务的管理从业务逻辑中分离出来,通过配置的方式来决定事务的处理方式。这样在代码的维护和扩展过程中,只需要修改事务传播行为的配置,而不需要修改业务逻辑代码,降低了代码的耦合度,提高了代码的可维护性和可扩展性。