目录
- 前言
- 事务的隔离级别
- 事务特性
- Spring 中设置事务隔离级别
- MySQL的隔离级别
- Spring中的隔离级别
- Spring的传播机制
- 事务传播机制是什么?
- 为什么需要事务传播机制?
- 事务传播机制有哪些?
- 事务的隔离级别 与 传播机制 解决的问题
前言
无论对于那个语言, 事务都是什么重要的 , 因此仅仅只了解, 事务是如何创建和使用是远远不够的 , 接下来我们讲述 事务的传播机制与 它的隔离级别
事务的隔离级别
事务特性
事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性和隔离性,具体概念如下:
原⼦性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中
间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个
事务从来没有执⾏过⼀样。
⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完
全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯
作。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务
并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。
事务隔离分为不同级别,包括读未提交(Readuncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化(Serializable)。
上⾯ 4 个属性,可以简称为ACID
而在这个四个特性中只有隔离级别是我们可以设置的, 为什么要设置隔离级别呢?
答案: 设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的
Spring 中设置事务隔离级别
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置,具体操作如下图所示
MySQL的隔离级别
我们都知道MySQL的隔离级别有 四种 , 分别如下
-
READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
-
READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
-
REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读(Phantom Read)。
-
SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多
● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据
Spring中的隔离级别
在Spring中的隔离级别有5中, 其中四种就是mysql的隔离级别, 多了一种是Default 默认, 也就是跟随默认的数据库的隔离级别
小问题: 如果数据库 和 Spring都设置了 隔离级别 并且不相同的话, 以谁为主?
答: 以Spring中的隔离级别为主
Spring的传播机制
事务传播机制是什么?
Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。
为什么需要事务传播机制?
是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)
事务传播机制有哪些?
- Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
要么一起提交, 要么一起回滚 , 如果 注意 是存在一条事务链的, 如图, 假如事务ABC 都使用的REQUIRED , 那么事务A 在调用时, 会先看这条事务链上有没有事务, 没有的话 A就变成了事务链的源头 , BC 在调用的时候, 就在A的事务链上继续加入, 一旦这个事务链上有事务出现异常了, 就整体事务链都回滚
- Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
还是上述的图片 , 如果如果事务A在调用时, 发现没有存在调用链, 那么即使事务A 有设置了传播机制, 事务A 同样以 无事务的方式运行, BC同理
- Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
- Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
这个的意思是不管你有没有事务, 我都要新开启一个事务链, 事务链互不干扰
-
Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
-
Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
-
Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED
最后一个嵌套比较难懂, 嵌套的意思是说, 如果存在事务链 ,就在事务链上的基础 在开启一条事务链 ,相当于原来事务链上的一个分支, 如果这个分支上发生异常 了 也只会回滚 分支, 而不涉及主干, 注意 如果有事务 ,是每一个事务都会创建一个分支 , 在分支的基础上创建分支
事务的隔离级别 与 传播机制 解决的问题
事务的隔离级别 解决的是 : 多个事务 调用 一个 数据库的问题(是保证多个并发事务执⾏的可控性的)
事务的传播机制 解决的是 : 多个事务在相互调用的时候, 事务的执行行为