Spring事务传播机制

一、事务特性

数据库事务(> transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

上段是引用自百度百科的内容,由此可知,事务是一个原子性操作,事务具有一下四个性质(ACID):

  1. 原子性(Atomicity):要么全部执行,要么全部不执行
  2. 一致性(Consistency):指事务执行前后,数据都不必须保持一致性状态,比如A、B之间的总金额是1W,那么无论他们直接转账多少次,那么总的金额都应该是1W。
  3. 隔离性(Isolation):事务的执行不受到其它事务的影响,但是事务执行的中间结果对其它事务是透明的
  4. 持久性(Durability):事务一旦提交,数据就必须持久化到硬盘上

由事务的隔离性,又引出了事务的隔离级别,首先我们看看事务隔离性中引出的问题:

  1. 脏读:指的是一个事务读取了另外一个事务未提交的数据,比如A事务对某一项数据进行了修改,但没有提交,此时B事务来读取了这个修改后的值,A因为执行存在异常,对事务进行了回滚操作,数据项又变回了原来的值,所以事务B读到的数据存在错误,也就产生了脏读。比如库存操作,假设现在有10个商品,A事务执行了减一操作(变成了9,但是没有提交),此时B事务,读取到的数据就是9,如果A此时对事务进行回滚,那么库存又变成了10,此时B利用读取到的数据进行操作,那么就是一个脏数据
  2. 不可重复读:事务A先读取了数据,此时事务B更新了此数据项并提交事务,而事务A再次读取之后,发现跟原来读取的数据存在不一致,这就是不可重复读,它与脏读的区别在于,脏读是读取了另一个事物未提交的数据,不可重复读是读取了另一个事物提交的数据,不可重复读在于一个事务多次读取同一数据项得到不同结果
  3. 幻读:幻读一般针对于增加和删除操作,比如事务A,针对某个数据集进行更新操作(比如年龄等于20),此时事务B又新增了一条数据(年龄等于20),此时事务A发现原本已经更新完的数据此时又多出一条(仿佛出现了幻觉),再比如,事务A删除了所有名字叫Jack的用户信息,此时事务B又新增了一条叫Jack的用户,而事务A再次读取的时候发现原本应该删除的数据,还有一条为删除。幻读和不可重复读,都是读取了提交的事务,不可重复读在于读取的是同一条数据,二幻读在于一批数据,或者说原本不该出现的数据出现了。

针对这几种问题,那么怎么去解决呢?
1、加锁

锁机制:共享和排它,共享锁表示其它事务可以读但不可以写,排它锁表示其它事务不可以读也不可以写
锁的粒度:表锁、行锁,表锁是锁住整个表,加锁和释放锁都很快,实现简单,但是会造成资源竞争激励,行锁锁粒度最小,加锁满,竞争小

脏读:是读取了另外事务未提交的操作,如果此时在事务A更新数据时,如果加上排它锁,此时事务B去读取的时候,只能等待事务A提交之后,才能继续读取。也就是可以通过行锁和排它锁来解决脏读问题

不可重复读:不可重复读是读取了以提交的数据,如果事务A在读取数据的时候加上共享锁,此时事务B去修改的时候,只能等待事务A读取完毕释放之后,事务B才能进行更新操作。这样就可以避免事务A在读取数据的时候出现不一致的情况

幻读:脏读和不可重复读可以通过行锁、共享和排它锁来解决,但是针对幻读情况,则不行,假设事务A读取了一批数据,并且加上了行锁,此时其它事务要去操作这些数据的时候只能等待当前事务A执行完毕才可以,但是如果是新增数据的时候,此时却不受行锁限制,此时需要在行锁的基础上增加表锁,锁住整张表。使事务进行串行化方式执行。

这些也对应着四种隔离级别:
读未提交(read uncommitted):最低级别,会产生脏读
读已提交(read commited):解决了脏读
可重复读(repeatable read):解决了不可重复读
串行化(Serialable):解决了幻读(加表锁)

可重复读:针对数据项会加上间隙锁(会针对一个区间加锁,哪怕区间中部分数据不存在,比如数据库中有id为3 ,5, 6 ,7的数据项,此时读取这些数据项的时候加上间隙锁,如果另外一个事务新增一个id为4或者删除id为3,5,6,7的行为都是不允许的,所以一定程度上解决了不可重复读,但是新增操作可能跳出这个区间范围)

2、MVCC

多版本并发控制,在mysql中主要是为了提高数据库的并发性能关于这部分内容可以参考文章,接下来简单阐述一下其工作流程,我们在创建表的时候,系统会自动增加三列隐藏列,ID(用户没有创建系统自动创建一个索引id)、DATA_TRX_ID(事务id)、DATA_ROLL_PTR(回滚指针)。trx_id用来保存每个事务执行的事务id,roll_ptr用来指向每次修改的版本,新增一条记录时,指针为空,当另外一个事务对这条记录进行修改时,会先把原来的数据保存到undo.log日志文件中,修改后的数据中的roll_ptr指向修改前在undo中的地址,如果有更多的事务对当前数据进行修改,就会形成一个版本链。

为了实现MVCC还有一个概念就是ReadView(保存着当前系统中还有哪些活跃的事务,把这些活跃的事务id保存在集合中比如叫trx_ids)。此时一个事务去读取数据,会得到当前数据的一个快照,然后根据读取到的数据trx_id值来判断,如果当前的trx_id值比trx_ids中最小值还小,则说明当前事务对这份快照数据可见(也就是当前数据可用),如果大于最大的事务id,则说明当前快照的数据,在ReadView生成后才生成,所以当前事务不可见,如果在之间,则判断是否在这个集合中,在则不可见,否可见。

比如,事务隔离级别中的读已提交(read commited),这种隔离级别下可以防止脏读,假设A事务去读取某条数据,此时在读取之前已经有B(id=100)事务进行了修改,但是没有提交,此时A事务读取到的ReadView中的集合就是100,此时就会A事务拿到的数据中的事务id就是100,然后和ReadView中的数据进行对比,发现存在,则表明当前这个数据是不可见的(也就是不可用)然后根据roll_ptr指针进行跳转到上一个版本,此时发现得到的数据要比100小,那么就是可以使用的。此时说明read-commited下是读取不到还未提交的数据的。

如果在REPEATABLE READ隔离级别下,会在第一次读取的时候生成一个ReadView,后续读取的时候就不会在生成,假设A事务读取之前,有B(id=100)、C(id=200)两个事物执行更新操作(都未提交),此时A读取到的ReadView中的集合就是[100,200],假设事务把先执行了更新,但是没有提交此时事务A才开始读取,此时根据判断当前的事务id在集合中,不满足进行根据指针链进行跳转,直到满足为止,如果此时B提交,然后C执行了更新(没有提交),因为ReadView只会在第一次读取的时候生成,所以表中的集合还是[100,200],此时再判断当前是200,也在集合中不满足,继续指针跳转,指向100的时候还是存在,继续跳转直到满足,此时也就解决了不可重复读的问题。

二、Spring中的事务传播

Spring中事务的传播机制,主要是指多个事务方法相互调用过程中,事务在方法间的传递过程,比如方法A,调用了方法B(A、B都是事务性调用),此时不管是在方法A还是在方法B中出现了异常,那么事务回滚状态是怎样进行的。

Spring中事务的传播行为主要分为七类(以A、B方法调用为例,A调用了B):

  1. REQUIRED:默认值,表示当前不存在事务的时候,则新建一个事务,如果当前存在事务,采用当前事务,也就是A方法本身是一个事务,那么B方法如果也是一个事务提交,那么此时B方法的事务会沿用A方法的事务
  2. SUPPORTS:如果当前存在事务,加入事务,如果当前不存在事务则以非事务执行,如果B方法上的传播机制是SUPPORTS,如果此时A方法是一个事务执行,那么B方法也会是一个事务执行,否则就是非事务执行。
  3. MANDATORY:如果当前存在事务,则加入当前事务,否则跑出异常
  4. REQUIRES_NEW:可以理解为A方法中无论是否有事务,B方法都会开启一个新的事务,也就是当前方法都会以新事务执行
  5. NOT_SUPPORTED:始终以非事务执行,如果当前存在事务,则挂起
  6. NEVER:不使用事务,如果当前存在事务抛出异常
  7. NESTED:如果当前事务存在,则嵌套执行否则和REQUIDRED一样

以上具体内容可以参考

以上有任何不对的地方,请留言指正,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟+1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值