事务隔离级别和事务传播机制
事务隔离级别
事务隔离级别是数据库管理系统在处理多个事务时,用来隔离并发事务以防止数据不一致性的机制。不同的隔离级别提供了不同的保护级别,以防止脏读、不可重复读和幻读等并发问题。以下是SQL标准中定义的四种隔离级别,以及一个额外的ISOLATION_DEFAULT
级别
1. ISOLATION_DEFAULT隔离级别
- 定义:这是PlatformTransactionManager默认的隔离级别,它使用数据库默认的事务隔离级别。
- 特点:不同的数据库系统可能有不同的默认隔离级别。例如,Oracle数据库的默认隔离级别是READ_COMMITTED,而MySQL数据库的默认隔离级别是REPEATABLE_READ。
2. ISOLATION_READ_UNCOMMITTED隔离级别(读未提交)
- 定义:这是事务最低的隔离级别,允许一个事务读取另一个事务未提交的数据。
- 特点:
- 会产生脏读、不可重复读和幻像读。
- 由于可以读取未提交的数据,这种隔离级别可能会导致数据不一致。
- 很少使用,因为它提供了最低的数据一致性保障。
3. ISOLATION_READ_COMMITTED隔离级别(读已提交)
- 定义:保证一个事务修改的数据提交后才能被另一个事务读取。
- 特点:
- 可以避免脏读,因为只能读取已提交的数据。
- 可能会出现不可重复读和幻像读。例如,在一个事务中读取了某条记录后,另一个事务对该记录进行了修改并提交,那么再次读取该记录时,会得到不同的结果。
- 是大多数情况下的推荐值,因为它提供了较好的数据一致性和并发性能平衡。
4. ISOLATION_REPEATABLE_READ隔离级别(可重复读)
- 定义:这种事务隔离级别可以防止脏读和不可重复读。
- 特点:
- 除了保证一个事务不能读取另一个事务未提交的数据外,还保证了在同一个事务中多次读取同一数据时,能够得到相同的结果。
- 可能会出现幻像读。例如,在一个事务中读取了满足某条件的所有记录后,另一个事务插入了新的满足该条件的记录并提交,那么再次读取时会得到更多的记录。
- 提供了比READ_COMMITTED更高的数据一致性保障。
5. ISOLATION_SERIALIZABLE隔离级别(可串行化)
- 定义:这是花费最高代价但最可靠的事务隔离级别,事务被处理为顺序执行。
- 特点:
- 除了防止脏读、不可重复读外,还避免了幻像读。
- 通过将事务完全隔离,确保每个事务在执行时不会受到其他事务的干扰。
- 会严重影响程序的性能,因为事务需要按顺序执行,无法并发处理。
- 通常情况下不会用到该级别,除非对数据一致性有极高的要求
事务传播机制
枚举类Propagation结合与@Transactional注解使用,枚举里面定义的事务传播行为类型与TransactionDefinition接口中定义的事务传播类型相对应,在使用@Transactional注解时,使用的是Propagation枚举类型中的事务传播类型,并不是直接使用的TransactionDefinition接口中定义的事务传播类型。
public enum Propagation {
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* {@code SUPPORTS} is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code jakarta.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Jakarta EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code jakarta.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Jakarta EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager. Some JTA providers might support nested
* transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
1. REQUIRED:
- 这是最常见的传播级别。如果当前存在事务,则加入该事务;如果当前没有事务,就新建一个事务。
- 适用场景:这是最常用的事务传播行为,适用于大多数需要事务管理的场景。
- 注意:外部不存在事务时,开启新的事物;外部存在事务时,加入到外部事物中,并且如果调用端发生异常,则调用端和被调用端的事物都将会回滚。在这种事务传播类型下,当前操作必须在一个事务中执行。
2. REQUIRES_NEW:
- 总是创建一个新的事务,如果当前存在事务,就把当前事务挂起。
- 适用场景:当需要确保操作在一个全新的事务中执行,不受现有事务的影响时使用。例如,在记录日志或审计信息时,可能希望这些操作独立于主业务逻辑的事务之外。
- 注意:新创建的事物和被挂起的事物没有任何关系,他们是两个不相干的独立的事物。外部事物失败回滚,不会回滚内部事务的执行结果。内部事务失败抛出异常,被外部事物捕获到时,外部事物可以不处理内部事务的回滚操作。
3. SUPPORTS:
- 如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行。
- 适用场景:那些不严格要求事务的场景,适用于读取数据等对数据一致性要求不高的场景。
- 注意:外部不存在事务时,不会开启新的事务;外部存在事务时,加入到外部事务中。
4. MANDATORY:
- 如果当前存在事务,则加入该事务;如果当前没有事务,就抛出异常。
- 适用场景:当方法必须运行在事务上下文中时使用,如果不在事务中调用会抛出异常。
- 注意:这种事务传播类型具备强制性,当前操作必须存在事务,如果当前操作不存在事务,则抛出异常
5. NOT_SUPPORTED:
- 总是非事务地执行,如果当前存在事务,就把当前事务挂起。
- 适用场景:当执行的操作不需要事务支持,或者执行非事务操作比在事务中执行更高效时使用。
- 注意:如果当前操作存在事务,则把事务挂起,以非事物的方式运行。
6. NEVER:
- 总是非事务地执行,如果当前存在事务,则抛出异常。
- 适用场景:那些绝对不能在事务中执行的操作。
- 注意:
- NEVER事务传播类型和NOT_SUPPORTED事务传播类型的区别是:如果当前存在事务,则NEVER事务传播类型会抛出异常,而NOT_SUPPORTED事务传播类型会把当前事务挂起,以非事务的方式运行。
- NEVER事务传播类型与MANDATORY事务船舶类型的区别是:NEVER事务传播类型表示如果当前操作存在事务,则抛出异常;而MANDATORY事务传播类型表示如果当前操作不存在事务,则抛出异常。
7. NESTED:
- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,行为与
REQUIRED
相同。 - 适用场景:适用于需要在现有事务基础上添加子事务的情况,子事务可以独立提交或回滚而不影响父事务。
常用的事务传播类型:
- REQUIRED:这是默认的事务传播级别,适用于大多数需要事务管理的场景。
- REQUIRES_NEW:适用于需要创建一个与当前事务完全隔离的新事务的场景,比如在当前事务中需要执行一些完全独立的数据库操作。
- SUPPORTS:适用于那些不严格要求事务的环境,如果存在事务就使用,不存在则不使用。
- NOT_SUPPORTED:适用于那些不应该在事务中执行的操作,比如读取配置信息等。