Spring的事务传播机制学录
博客书
版本说明
spring.boot=2.2.5.RELEASE
相关链接:
- spring 事务官方说明:https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/data-access.html#spring-data-tier
- 验证代码地址:https://github.com/simba1949/spring-transactional-learn.git
Spring 事务传播说明(主)
- 在不同类中各个事务具有事务传播特性,非事务方法调用是事务方法有事务产生;事务方法调用事务方法,事务会在调用者和被调用者进行传播;
- 在同一个类中,非事务方法调用事务方法无任何事务产生;事务方法调用事务方法只对当前调用方法产生事务;
验证代码Github地址:https://github.com/simba1949/spring-transactional-learn.git
备注:运行代码需要查看控制日志
- 事务创建:Creating new transaction with name
- 事务提交:Initiating transaction commit
- 支持当前事务:Participating in existing transaction
- 事务挂起:Suspending current transaction
- 事务恢复:Resuming suspended transaction
Spring 事务管理
数据库事务(Database Transaction)是指将一系列数据操作当做一个逻辑处理单元的操作,这个单元中的数据库操作要么完全执行,要么完全不执行。通过将一组相关操作组合作为一个逻辑处理单元,可以简化错误恢复,并使应用程序更加可靠。
事务的特性
- 原子性(Atomicity):一个事务内的操作,要么全部执行成功,要么全部执行不成功。
- 一致性(Consistency):事务执行后,数据库状态与其他业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和是不变的。
- 隔离性(Isolation):每个事务独立运行。在并环境下,并发的事务是互相隔离的,互不影响。
- 持久性(Durability):事务一旦提交后,数据库中的数据必须被永久地保存下来。
事务的隔离级别
事务隔离级别越高,并发性能越差,但是安全性越高。
MySQL 事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
read_uncommited (读未提交) | √ | √ | √ |
read_commited (读已提交) | × | √ | √ |
repeatable_read (可重复读):默认 | × | × | √ |
serializable (串行化) | × | × | × |
Oracle 事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
read_uncommited (读未提交) | √ | √ | √ |
read_commited (读已提交):默认 | × | √ | √ |
serializable (串行化) | × | × | × |
Spring 事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
isolation_default | 同数据库事务隔离级别 | 同数据库事务隔离级别 | 同数据库事务隔离级别 |
isolation_read_uncommited | √ | √ | √ |
isolation_read_commited | × | √ | √ |
isolation_repeatable_read | × | √ | √ |
isolation_serializable | × | × | × |
read_uncommited
读未提交,即一个事务读取到另一个事务未提交的数据。在 read_uncommited
隔离级别下,会造成“脏读”;
假设场景,有 A、B两个事务同时对一个账户进行存款和取款操作,A 事务向账户存款 10元,B 事务从账户取款 10元。
时间轴 | 事务A存款 | 事务B取款 |
---|---|---|
T1 | 事务A开始 | —— |
T2 | —— | 事务B开始 |
T3 | —— | 事务B查询余额(余额为10元) |
T4 | —— | 事务B取出10(余额为0元) |
T5 | 事务A查询余额(余额为0元) | —— |
T6 | —— | 事务B撤销(余额为10元) |
T7 | 事务A存入10元 | —— |
T8 | 事务A提交(余额更新为10元) |
正常情况下,A、B事务执行完之后,账户余额应为20元,但是在时刻T5时,事务A查询到的余额为0元,这是因为读到事务B未提交的数据,即脏读。
read_commited
读已提交,在 read_commited
隔离级别下,可以有效避免“脏读”。虽然解决了“脏读”问题,但是无法避免不可重复读。
假设场景
时间轴 | 事务A查询 | 事务B取款 |
---|---|---|
T1 | 事务A开始 | —— |
T2 | —— | 事务B开始 |
T3 | —— | 事务B查询余额(余额为10元) |
T4 | 事务A查询余额(余额为10元) | —— |
T5 | —— | 事务B取出10元(余额为0元) |
T6 | —— | 事务B提交 |
T7 | 事务A查询余额(余额为0元) | —— |
T8 | 事务A提交 |
事务A执行了两次余额查询,但第一次查询得到的余额为10元,第二次查询得到的余额为0元,这就是不可重复读的问题。
repeatable_read
可重复读级别是保证在事务处理过程中多次读取统一数据时的值始终是一致的。可重复读取是通过在事务开启后不允许其他事务对当前记录进行修改操作。
repeatable_read
隔离级别可以有效地避免“脏读”和不可重复读的问题,但是有可能会出现“幻读”。
假设场景
时间轴 | 事务A查询记录 | 事务B取款 |
---|---|---|
T1 | 事务A开始 | —— |
T2 | —— | 事务B开始 |
T3 | 查询交易记录 | —— |
T4 | —— | 事务B存入10元 |
T5 | —— | 事务B提交 |
T6 | 查询交易记录 | —— |
T7 | 提交事务 | —— |
在事务A中,同一个事务多次获取交易记录,发现第二次获取交易记录的结果中多存了一笔存款记录(事务B发生的存款操作),对于事务A来说,好像出现了幻觉一样,即“幻读”。
serializable
串行化是最严格地事务隔离级别。它要求所有的事务排队依序执行,即事务只能一个接一个的处理,不能并发执行。
假设场景
时间轴 | 事务A存款 | 事务B取款 |
---|---|---|
T1 | 事务A开始 | —— |
T2 | 事务A查询余额(余额为0元) | —— |
T3 | 事务A存入10元 | —— |
T4 | 事务A提交 | —— |
T5 | —— | 事务B开始 |
T6 | —— | 事务B查询余额(余额为10元) |
T7 | —— | 事务B取出10元 |
T8 | —— | 事务B提交 |
Spring七种事务传播行为
https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/data-access.html#tx-propagation
事务传播行为是用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时候。
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新创建一个事务。 如果已经存在一个事务中,加入到这个事务中。 |
PROPAGATION_SUPPORTS | 支持当前事务。如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前事务。如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新创建事务。如果当前存在事务,就把当前事务暂时挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作。如果当前存在事务,就把当前事务暂时挂起。 |
PROPAGATION_NEVER | 以非事务方式执行操作。如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。 如果当前没有事务,则执行 PROPAGATION_REQUIRED 类似操作与 PROPAGATION_REQUIRES_NEW 的差别 PROPAGATION_REQUIRES_NEW 另起一个事务,将会与其父事务互相独立。PROPAGATION_NESTED 事务和其父事务是相依的,其要等父事务一起提交。 |
Spring 事务传播说明
- 在不同类中各个事务具有事务传播特性,非事务方法调用是事务方法有事务产生;事务方法调用事务方法,事务会在调用者和被调用者进行传播;
- 在同一个类中,非事务方法调用事务方法无任何事务产生;事务方法调用事务方法只对当前调用方法产生事务;