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) | 不可能 | 不可能 | 不可能 |
不同数据库中的默认隔离级别有所不同:
- MYSQL: 默认为REPEATABLE_READ级别
- SQLSERVER: 默认为READ_COMMITTED
- 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的事务中。
- Spring中使用
PROPAGATION_REQUIRED
作为默认传播行为 - Spring事务传播行为中
PROPAGATION_REQUIRED
和PROPAGATION_NESTED
是比较像的,对于下面示例代码,funB()上使用PROPAGATION_REQUIRED
和PROPAGATION_NESTED
都会引起funB回滚
serviceA{
@Transactional
funcA(){
serviceB.funcB();
int i = 1/0; // 模拟异常报错
}
}
serviceB{
@Transactional
funcB(){
// 执行insert或update操作
}
}
- 在上面的示例代码中,如果funB使用
PROPAGATION_REQUIRES_NEW
, 则funcB将会启动一个独立事务,此时funcB提交成功后,funA的异常不会引起funB的回滚。