初识事务,事务隔离级别,事务传播行为

本篇文章会介绍如下几个概念:事务,事务隔离级别,spring事务的传播模式。在介绍事务时会引出原子性的概念,在介绍事务隔离级别的时候会引出脏读和幻读的概念。html

事务

什么是事务?

事务最开始是数据库中的概念,它把一系列的操做统一为一个总体,这一系列的操做要么同时成功,要么同时失败。一个事务基本的操做是:java

  1. 开启事务
  2. 若是发生了错误,进行回滚
  3. 若是没有发生错误,则提交事务

为何要有事务?

在咱们处理简单业务的时候,好比说一条插入数据的操做,只会获得两个结果,要么插入成功,要么插入失败,这对应到代码逻辑上是很简单的。
咱们称这样的操做具备原子性。spring

可是咱们的业务每每不会只有插入一条数据那么简单,可能用户点击一个按钮后,咱们须要插入一条数据和删除一条数据。因为每一个操做都有可能成功和失败,这个时候咱们就有了2^2=4种状况,这下编程起来就麻烦了。sql

为了方便编程(也为了符合实际业务逻辑),咱们引入开头所述的事务机制,把两个操做放在一个事务里面,使这两个操做具有原子性,这样一来业务处理起来就方便多了。数据库

事务隔离级别

上面咱们谈到了操做数据库的时候会使用到事务,接下来引入的问题就是:在数据库中,不免会出现多个事务同时操做数据的状况,这时数据库设置的事务隔离级别不一样,会出现不一样的数据操做结果,经典的脏读与幻读也诞生于此。编程

首先给出并发访问时,不一样事务隔离级别下的状况表:ubuntu

事务隔离级别 脏读 不可重复读 幻读
read uncommitted 读未提交
read committed 读提交 不会
repeatable read 可重复读 不会 不会
serializable 串行化 不会 不会 不会

能够看到事务隔离级别越高,产生的问题越少,可是相应的性能是会下降的,下面经过几个例子分别阐述不一样事务隔离级别下发生的问题:并发

读未提交ide

当事务隔离设置为读未提交时,最容易产生的问题是脏读。读未提交指的是当前事务能够读到其余事务未提交的数据:假设一位父亲给他的儿子打生活费,原本一个月2000可是不当心手抖多打了1000变成3000,这时儿子去商店消费,查询余额的时候就多了3000。因为父亲的事务还没提交,便立马回滚事务,从新打过去2000生活费。性能

这个时候儿子读到的余额就是脏数据,产生的缘由是读取了其余事务未提交的数据。

读提交

只要将事务隔离级别设置为读提交就能解决上面的脏读问题,他能保证当前事务只能读到其余事务已经提交的数据。可是读提交会面临一个新问题:不可重复读。

好比说儿子拿着卡到商店消费,买单的时候(开启当前事务),系统检测到卡里只有500元。这个时候父亲给儿子转了2000块生活费,当儿子准备扣费的时候再查询余额发现变成了2500元(这个查询发生在父亲的转帐事务提交以后)

在同一个事务中,儿子的余额在不一样的时候读取的值不同,这就是不可重复读问题。想要解决这个问题,须要把事务隔离级别设置为可重复读

可重复读

在可重复读的状况下,当前事务会禁止其余事务对正在操做的数据进行更新,这样一来,父亲转帐的事务就要等到儿子帐号扣费结束后才能进行,今后解决了不可重复读问题。

可是可重复读级别下还可能发生一个问题叫幻读,举例以下:儿子今天在外消费了1000元,父亲查看儿子一天的消费记录(开启事务),发现一共是1000元。这个时候儿子又消费了1000元(父亲的事务仍在进行中),接着父亲打印儿子今天的消费记录,发现莫名其妙地变成了2000元,多了一条消费记录。

像这种当前事务在操做的过程当中,因为别的事务增长或删除数据,致使当前事务操做的数据忽然变多或变少的状况,就叫幻读。想要解决幻读,须要把事务隔离级别升级为串行化

串行化

当事务隔离级别为串行化时,全部事务都是串行执行的,对应上面的例子:父亲在查看当天消费记录时,儿子是不能消费的。这么一来事务并发带来的问题都能解决,可是效率很低。

扩展

在经常使用的数据库中Orcale默认的事务隔离级别是读提交,而Mysql默认的是可重复读。在Mysql的InnoDB引擎中,虽然事务隔离级别是可重复读,可是也能够解决幻读问题,背后的原理是在数据行之间添加间隙锁,防止数据的插入与删除。

具体选择那一种事务隔离级别,要看具体的业务须要

事务传播行为

事务的传播行为从字面上也是挺好理解的:想要发生传播就必定要有两个以上的物体,而这里指的是两个方法都要在事务中进行,当一个事务方法A调用另外一个事务方法B时,另外一个事务方法B该如何运行。

Spring一共定义了7种事务传播行为(事务方法B该如何运行):

传播行为 含义
PROPAGATION_REQUIRED 若是当前没有事务,就新建一个事务,若是已经存在一个事务中,加入到这个事务中(这是最多见的选择,也是spring的默认事务传播行为)
PROPAGATION_SUPPORTS 支持当前事务,若是当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY 使用当前的事务,若是当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW 新建事务,若是当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操做,若是当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式执行,若是当前存在事务,则抛出异常
PROPAGATION_NESTED 若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做

平时咱们最经常使用的是 PROPAGATION_REQUIRED,这也是spring的默认事务传播行为,理解了它就能按理推导其余的事务传播行为。

好比说当前咱们有以下代码:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
     methodB();
    // do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}
  1. 假设咱们执行methodA,因为当前尚未事务,因而就新建立一个事务。
  2. 当在methodA中调用methodB的时候,因为methodA已经存在于事务中,因而methodB便无需新建立一个事务,直接加入到methodA的事务中便可。

总结

  1. 事务可以让一系列不一样的操做具备原子性。(固然事务具有ACID四大特性,本文在初步介绍时强调的是原子性)
  2. 事务隔离级别定义了事务并发操做时的访问规则。
  3. 事务传播行为定义了事务方法在执行时该怎么运用事务。

参考文章: 事务隔离级别:https://www.cnblogs.com/ubuntu1/p/8999403.html 事务传播行为:https://blog.csdn.net/weixin_39625809/article/details/80707695 事务传播行为:https://www.cnblogs.com/softidea/p/5962612.html

相关文章
相关标签/搜索