继续卷!面试又问Spring 事务有几种传播行为和隔离级别?
怕什么真理无穷
进一步有近一步的欢喜
面试又被问到了事务,来吧,要么卷起来,要么躺平。卷不动躺平会不会导致数据不一致?
事务概念
事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
说简单点就是,要么所有执行success,不然就fail。它最终的目标:数据不会被破坏。即事务操作成功,数据的结果和业务期待的结果是一致的。
事务的属性
一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性
原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durability)
1:原子性(Atomicity):原子性要求事务作为一个不可分割的整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行(其中有一个操作失败,就全部失败)。
2:一致性(Consistency):一致性要求事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
执行前数据间的一致性状态 === 执行后数据间的一致性状态
3:隔离性(Isolation):事务的隔离性要求多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
4:持久性(Durability):事务的持久性是一旦整个事务提交成功,数据的修改应该永久保存在数据库中,并不可逆转。
隔离性(Isolation)
事务指定了4种隔离级别(从弱到强分别是):
Read Uncommitted Read Committed Repeatable Read Serializable
在事务的并发操作中可能会出现脏读(dirty read),不可重复读(repeatable read),幻读(phantom read)。
1:Read Uncommitted(读未提交):一个事务可以读取另一个未提交事务的数据。
2:Read Committed(读提交):一个事务要等另一个事务提交后才能读取数据。
3:Repeatable Read(重复读):在开始读取数据(事务开启)时,不再允许修改操作。
4:Serializable(序列化):Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。
大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。MySQL的默认隔离级别是Repeatable read。
Spring事务的传播性
事务的传播级别和数据隔离级别,是事务控制的两个主要特性。传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。
Spring事务传播性有七种,REQUIRED、SUPPORTS、REQUIRES-NEW、NOT-SUPPORTED、MANDATORY、NEVER、NESTED。如下思维导图:
Spring事务的传播特性介绍:
PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启新的事物。 PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。 PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。 PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。 PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。 PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常 7.(spring)PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
Spring事务传播特性总结:
1.只要定义为spring的bean就可以对里面的方法使用@Transactional注解。 2.Spring的事务传播是Spring特有的。不是对底层jdbc的代理。 3.使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在[方法调用之前决定是否开启一个事务],并在[方法执行之后]决定事务提交或回滚事务。 4.Spring支持的PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别: PROPAGATION_REQUIRES_NEW:二个事务没有信赖关系,不会存在A事务的成功取决于B事务的情况。有可能存在A提交B失败。A失败(比如执行到doSomeThingB的时候抛出异常)B提交,AB都提交,AB都失败的可能。PROPAGATION_NESTED:与PROPAGATION_REQUIRES_NEW不同的是,内嵌事务B会信赖A。即存在A失败B失败。A成功,B失败。A成功,B成功。而不存在A失败,B成功。 特别注意PROPAGATION_NESTED的使用条件:使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。使用PROPAGATION_NESTED,还需 要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而 nestedTransactionAllowed属性值默认为false; 6.特别注意PROPAGATION_REQUIRES_NEW的使用条件:JtaTransactionManager作为事务管理器
Spring事务的隔离级别?
Spring事务的隔离级别:
ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。 ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。 ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。除了第一个是spring特有的,另外四个与JDBC的隔离级别相对应。第二种隔离级别会产生脏读,不可重复读和幻像读,特别是脏读,一般情况下 是不允许的,所以这种隔离级别是很少用到的。大多说数据库的默认格里基本是第三种。它能消除脏读,但是可重复读保证不了。第四种隔离级别也有一些数据库作 为默认的隔离级别,比如MySQL。最后一种用的地方不多,除非是多数据访问的要求特别高,否则轻易不要用它,因为它会严重影响数据库的性能
Spring事务的架构?
Spring 的事务框架设计理念的基本原则是:让事务管理的关注点与数据访问关注点相分离。
架构
Spring 的事务抽象包括3个主要接口,分别是PlatformTransactionManager、TransactionDefinition、TransactionSatus。
PlatformTransactionManager负责界定事务边界;TransactionDefinition负责定义事务的相关属性,包括隔离级别、传播行为等;PlatformTransactionManager参照TransactionDefinition的属性定义来开启相关事务。事务开启之后到事务结束期间的事务状态由TransactionStatus负责,我们可以通过TransactionStatus对事务进行有限的控制。
TransactionDefinition常用的实现有DefaultTransactionDefinition和TransactionTemplate(这两个主要用于编程式的事务场景)、DefaultTransactionAttribute和RuleBasedTransactionAttribute(这两个主要使用Spring AOP 进行声明式事务管理的场景中,RuleBasedTransactionAttribute允许我们同时制定多个回滚规则)。
TransactionStatus有一个实现类DefaultTransactionStatus用来记录事务的状态信息。PlatformTransactionManager的实现类可以分为面向局部事务和面向全局事务两个分支。常用的面向局部事务的PlatformTransactionManager有DataSourceTransactionManager(用于JDBC和Mybatis)和HibernateTransactionManager。
使用Spring如何进行事务管理?
事务管理配置
编程式事务 声明式事务
编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
欢迎关注微信公众号:互联网全栈架构,收取更多有价值的信息。