提示:若对事务基本概念了解的,可直接看一下事务的隔离性和传播性。
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一,事务是什么?
事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。
二,事务的特点(ACID)
综上对事务的定义,抽离一下四个特点:
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。 也就是说事务前后数据的完整性必须保持一致。
隔离性(Isolation) 隔离性是指一个事务的执行不能有其他事务的干扰,事务的内部操作和使用数据对其他的并发事务是隔离的,互不干扰。
持久性(Durability)
持久性是指一个事务一旦提交,对数据库中数据的改变就是永久性的。此时即使数据库发生故障,修改的数据也不会丢失。接下来其他的操作不会对已经提交了的事务产生影响。
三,事务的实现原理以及使用
Mysql实现原理:
我之后再开一篇内容进行说明
Spring事务管理:
Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编码式(不常用,粒度更细,)和声明式(常用,便捷)的两种方式。
注解的方式:
在对应方法上加上@Transactional即可;
@Transactional(rollbackFor = Exception.class //回滚事务
,propagation = Propagation.REQUIRED, //事务传播行为
isolation = Isolation.DEFAULT, //事务隔离级别
readOnly = true //是否有事务
)
这里只是点出了注解声明式的使用方式,xml声明式以及编码式TransactionTmeple,感兴趣自行了解;
以上基本是了解事务的基本使用,接下来着重说明一下在springboot中事务的隔离性与传播性;(很容易让新手迷糊的概念)
四,事务的隔离性
理解事务的隔离性之前,需要了解隔离是为了处理什么问题?什么现象?
事务在高并发的时候会出现:脏读、幻读、不可重复读
脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
比如在事务 A 修改数据之后提交数据之前,这时另一个事务 B 来读取数据,如果不加控制,事务 B 读取到 A 修改过数据,之后 A 又对数据做了修改再提交,则 B读到的数据是脏数据,此过程称为脏读。
不可重复读
不可重复读是指在数据库访问中,一个事务范围内多次查询却返回了不同的数据值。这是由于在查询间隔中,其他事务修改并提交而引起的。比如事务 T1 读取某一数据,事务 T2 读取并修改了该数据,T1 为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
幻读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
比如事务 A 在按查询条件读取某个范围的记录时,事务 B 又在该范围内插入了新的满足条件的记录,当事务 A 再次按条件查询记录时,会产生新的满足条件的记录。
幻读和不可重复读再概念上相似,都是在事务处理过程中,会有同一条件多次查询的动作,一般情况下,同一查询条件,查出的数据结果应该是一样的。但由于并发的情况下,会出现同一查询条件,多次查询不一致的情况;
【区别】
幻读,是查到的数据条目数不一致; 如A事务中第一次查出10条,第二次查出了20条;
不可重复读,是查到同一条数据的数据内容改变了;如A事务中第一次查id为1的数据:name=张三 ,第二次查id为1的数据:name=李四;
一个是数据内容 , 一个是数据条目数.
为了处理以上出现的问题,从而提出了事务的隔离级别(本质是加锁)
事务的隔离级别
未提交读(Read Uncommitted) 无锁
一个事务能够读取到别的事务中没有提交的更新数据。事务中的修改,即使没有提交,其他事务也可以看得到。在这种隔离级别下有可能发生脏读,不可重复读和幻读。提交读(Read Committed) 锁事务
事务中的修改只有提交以后才能被其它事务看到。在这种隔离级别下解决了脏读,但是有可能发生不可重复读和幻读。可重复读(Repeated Read) 锁行
保证了在同一事务中先后执行的多次查询将返回同一结果,看到的每行的记录的结果是一致的,不受其他事务的影响。但是这种级别下有可能发生幻读。可串行化(Serializable) 锁表
不允许事务并发执行,强制事务串行执行。就是在读取的每一行数据上都加上了锁,读写相互都会阻塞,所以效率很低下。这种隔离级别最高,是最安全的,但是性能最低,不会出现脏读,不可重复读,幻读。
以上知道了在并发情况下,事务间可能出现的并发问题,从而引出事务间的隔离,也就是事务间的事务行为隔离;
事务内的事务又会有什么行为嘞?事务的传播方式;
五,事务的传播方式
1、PROPAGATION_REQUIRED:(Spring默认的事务传播类型 required:需要、依赖、依靠):
如果当前没有事务,则自己新建一个事务,如果当前存在事务则加入这个事务(这是最常见的选择)
当A调用B的时候:如果A中没有事务,B中有事务,那么B会新建一个事务;如果A中也有事务、B中也有事务,那么B会加入到A中去,变成一个事务,这时,要么都成功,要么都失败。(假如A中有2个SQL,B中有两个SQL,那么这四个SQL会变成一个SQL,要么都成功,要么都失败)2、PROPAGATION_SUPPORTS:(supports:支持;拥护)
当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败),如果A中没有事务,那么B就以非事务方式运行(执行完直接提交);3、PROPAGATION_MANDATORY:(mandatory:强制性的)
当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败);如果A中没有事务,B中有事务,那么B就直接抛异常了,意思是B必须要支持回滚的事务中运行4、PROPAGATION_REQUIRES_NEW:(requires_new:需要新建)
创建一个新事务,如果存在当前事务,则挂起该事务。
B会新建一个事务,A和B事务互不干扰,他们出现问题回滚的时候,也都只回滚自己的事务;5、PROPAGATION_NOT_SUPPORTED:(notsupported:不支持)
以非事务方式执行,如果当前存在事务,则挂起当前事务
被调用者B会以非事务方式运行(直接提交),如果当前有事务,也就是A中有事务,A会被挂起(不执行,等待B执行完,返回);A和B出现异常需要回滚,互不影响6、PROPAGATION_NEVER NEVER(never:从不):
如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。就是B从不以事务方式运行
A中不能有事务,如果没有,B就以非事务方式执行,如果A存在事务,那么直接抛异常7、PROPAGATION_NESTED:(nested:嵌套的)
嵌套事务:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
如果A中没有事务,那么B创建一个事务执行,如果A中也有事务,那么B会会把事务嵌套在里面。
对于事务的传播方式中的PROPAGATION_NESTED(嵌套)字面理解起来有点模糊,单独拎出来说明一下:
【PROPAGATION_REQUIRES_NEW 】启动一个新的, 不依赖于环境的 “内部” 事务.这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等.当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
【PROPAGATION_NESTED】 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint.嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
以表格的形式进行比对:
异常状态 | REQUERES_NEW(两个独立事务) | REQUERED(AB合并为一个事务) | NESTED(B嵌套在A事务中) |
---|---|---|---|
A异常,B正常 | A挂起,B正常提交,A回滚 | AB回滚 | B未提交,A回滚 |
A异常,B异常 | A挂起,B先回滚,A再回滚 | AB回滚 | B回滚至savepoint,A回滚 |
A正常,B异常 | A挂起,B回滚,A再提交 | AB回滚 | B回滚至savepoint,A提交 |
A正常,B正常 | A挂起,B提交,A再提交 | AB提交 | B执行完挂起,统一由A提交 |
总结
首先要了解事务的定义是什么?ACID
在springboot中如何对事务进行管理?
然后在使用过程中会出现什么问题?
以及如何解决这些问题,现有的解决方法有哪些?
参考:
springboot事务详解