事务
相关知识点:MySQLSpringBoot
什麽是事务?
是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体向系统提交,要么都成功,要么都失败。事务是一组不可再分割的操作集合(工作逻辑单元)
事务的四大特性
原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
原子性 Atomicity
- 最小工作逻辑单元,要么一起成功,要么一起失败。
一致性 Consistency
- 事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到。
隔离性 Isolation
- 别人看的时候不能改
- 别人改的时候不能看
- 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
持久性 Durability
- 数据库中事务被提交,数据会永久保存
- 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
事务的隔离级别
读未提交 Read Uncommitted : 读取到未提交的数据 脏读
- 一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据脏读
- 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
读已提交 READ COMMITTED : 只能读取到已经提交的数据 不可重复读
- 读已提交是大多数主流数据库的默认事务等级,保证了一个事务不会读取到另一个并行事务已修改但未提交的数据,避免了脏读取。
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
可重复读 REPEATABLE_READ : 两次读取的数据不一致 幻读
- 保证一个事务不会修改另一个事务读取但未提交(回滚)的数据。避免了脏读和不可重复度的情况,但是带来了更大的性能损失。
- 在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象
- 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
串行化 Serializable :
- 最高的隔离级别,在这个隔离级别下,不会产生任何异常。并发的事务,就像事务是在一个个按照顺序执行一样
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
需要特别注意以下几点
- MySQL默认的事务隔离级别为repeatable-read
- MySQL 支持 4 种事务隔离级别
- 事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持.
- Oracle 支持的 2 种事务隔离级别 READ_COMMITED , SERIALIZABLE
- SQ规范锁规定的标准,不同的数据库具体的实现可能会有些差异
- MySQL中默认数据隔离级别是
可重复读
时,并不会锁住读取到的行。 - 事务隔离级别: 未提交读时,写数据只会锁住相应的行。
- 事务隔离级别为: 可重复读时,写数据会锁住整张表。
- 事务隔离级别为: 串行化时,读写数据都会锁住整张表。
- 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得啊。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed ,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
事务的传播机制
事务传播行为
1.PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2.PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3.PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4.PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
5.PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6.PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7.PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。、
代码展示
待补充~
嵌套事务
什么是嵌套事务?
- 嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:
如果子事务回滚,会发生什么?
- 父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
如果父事务回滚,会发生什么?
- 父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:
事务的提交,是什么情况?
- 是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。
- 参考文章:https://blog.csdn.net/liangxw1/article/details/51197560
Spring中的事务
编程式事务
编程式事务是指在代码中手动管理事务的提交和回滚等操作,代码侵入性比较强
声明式事务
声明式事务是基于AOP切面实现的,他将具体的业务与事务进行解耦,代码的侵入性很低,所以在实际开发中是声明式事务用的比较多。声明式事务有两种实现实现方式,一种是基于TX和AOP的xml配置文件方式,第二种就是基于@Transactional注解了。
📋 @Transactional注解可以作用的地方
可以作用在接口
,类
,类方法
中
- 作用于类
- 当把@Transactional注解放在类上面时,表示该类所有的public方法都配置相同的事务属性信息。
- 作用于方法
- 当类配置了@Transactional,方法也配置了@Transactional时,方法的事务会覆盖类的事务配置信息。
- 作用于接口
- 不推荐~这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效
声明式事务的注意事项
- service调用异常上抛到controller。如果式编译时异常不会自动回滚,如果是运行时异常,会回滚
- 当前类下使⽤⼀个没有事务的⽅法去调⽤⼀个有事务的⽅法,没有事务的存在。如果是在本类中没有事务的⽅法来调⽤标注注解 @Transactional ⽅法,最后的结论是没有事务的
事物失效的场景
数据库引擎不支持事务
- 在MySQL数据库中有几种引擎(InnoDB,MyISAM,Memory等等),仅仅InnoDB支持事务,如果数据库底层都不支持事务的话,那么再怎么折腾都是白搭.
私有方法不支持事物
- @Transactional只能加在public方法上,如果需要在private方法中加入事务,可以使用Aspect配transactionManager使用。
- 为什么不支持?
入口的方法必须是public,否则事务不起作用(这一点由Spring的AOP特性决定的,理论上而言,不public也能切入,但spring可能是觉得private自己用的方法,应该自己控制,不应该用事务切进去吧)。另外private 方法, final 方法 和 static 方法不能添加事务,加了也不生效- 基于接⼝代理(JDK代理)
- 基于接⼝代理,凡是类的⽅法⾮public修饰,或者⽤了static关键字修饰,那这些⽅法都不能被Spring AOP增强
- 基于CGLib代理(⼦类代理)
- 基于⼦类代理,凡是类的⽅法使⽤了private、static、final修饰,那这些⽅法都不能被Spring AOP增强
- 基于接⼝代理(JDK代理)
本类方法调本类另一个方法
- @Transactional(propagation = Propagation.REQUIRES_NEW)是无效的,在Spring中是使用代理的方式实现事务,发生自身调用的时候,没有经过Spring的代理,自然事务失效.
不支持事物
- @Transactional(propagation = Propagation.NOT_SUPPORTED)表示如果当前存在事务就挂起,以没有事务的方式运行,主动不支持事务了,那么再怎么操作也是白搭. 此处贴下Spring的传播行为: