事务隔离级别

Posted saiqsai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事务隔离级别相关的知识,希望对你有一定的参考价值。

一、事务

1、定义

事务是一组原子性的SQL查询。如果数据库引擎能够成功的对数据库应用该组查询的全部语句,那么就执行该组查询;如果其中有任意一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。

2、特性

事务必须符合ACID特性。ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

(1)原子性

原子性是指事务是一个不可再分割的工作单位,整个事务中的操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分。

(2)一致性

数据库总是从一个一致性状态转换到另一个一致性状态。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。

一致性不太好理解,举个例子:

假设我们10个人,每人有一个账号,里面有钱,可以转来转去,这组成了一个小型的数据系统,那么什么叫数据一致性?这是由你自己来定义的,比较通用的就是:这10个人的账号金额总数不变——满足这一条件,就叫数据一致,不满足,就叫数据不一致,或者在分布式的环境下,有一个数据在几个地方都保存了,那么任何时候,这几个地方的数据都必须相同,这也叫一致性。
现在我们就这个简单的一致性规则:10个人的账号金额总数不变。假设初始的时候每个人账号里有一万,A账号往B账号里转5000,这时候数据库要执行两行代码:
A:减去5000
B:加上5000
在执行完第一行代码的时候,这时候数据是不满足一致性条件的!必须要执行完第二行代码,数据才恢复到一致性的状态!换而言之,数据库中的数据是经常处于不一致的状态,这是不可避免的,因此我们提出了事务的概念,用于检测数据库中的数据是否处于一致性状态——如果数据库中有没有执行完的事务,那就是不一致的,否则,就是一致的。

(3)隔离性

一个事务所做的修改在最终提交之前,对其他事务是不可见的。数据库事务可以设置不同的隔离级别。这个下一节会详细讨论。

(4)持久性

一旦事务提交,其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

二、隔离级别

1、数据库并发访问可能引起的问题

(1)第一类更新丢失(回滚丢失)

当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销。也就是说第一个事务做的跟新丢失了。 

时间 

取款事务A 

转账事务B 

T1 

开始事务 

  

T2 

  

开始事务 

T3 

查询账户余额为1000元     

  

T4 

  

查询账户余额为1000元 

T5 

  

汇入100元把余额改为1100元 

T6 

  

提交事务 

T7 

取出100元把余额改为900元 

  

T8 

回滚事务 

  

T9 

余额恢复为1000 元(丢失更新) 

 

(2)脏读(事务没提交,提前读取)

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

 

时间 

取款事务A 

转账事务B 

T1 

开始事务 

  

T2 

  

开始事务 

T3 

查询账户余额为1000元     

  

T4 

取出100元把余额改为900元 

 

T5 

 

查询账户余额为900元(脏读)  

T6 

 回滚事务 

 

T7 余额恢复为1000 元  

 

(3)不可重复读(两次读的不一致)

是指在一个事务内,多次读同一数据,在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

时间 

取款事务A 

转账事务B 

T1 

 

开始事务 

T2 

开始事务 

 

T3   查询账户余额为1000元 

T4

查询账户余额为1000元     

  

T5

取出100元把余额改为900元 

 

T6 提交事务  

T7

 

查询账户余额为900元(不可重复读)  

 

(4)第二类更新丢失(覆盖丢失)

  第二类更新丢失实在实际应用中经常遇到的并发问题,他和不可重复读本质上是同一类并发问题,通常他被看做不可重复读的特例:当多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新。因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的更新。

时间 

取款事务A 

转账事务B 

T1 

 

开始事务 

T2 

开始事务 

 

T3   查询账户余额为1000元 

T4

查询账户余额为1000元     

  

T5

取出100元把余额改为900元 

 

T6 提交事务  

T7

 

转出100元把余额改为900元(更新丢失)

(5)幻读 

是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

时间 

账户更新事务A

账户新增事务B 

T1 

开始事务 

 

T2 

 

开始事务 

T3 把所有账户的余额清0  

T4


 

插入一个新账户,设置余额为100

T5

 

提交事务

T6 获取账户余额,发现还有一个没清0(幻读)  


2、数据库的四种隔离级别

(1)读未提交(READ UNCOMMITTED)

 一个事务的修改,即使没有提交,对其他事务也都是可见的。这种隔离级别,一般在读数据时不会检查或使用任何锁。因此,会出现脏读问题。

(2)读已提交(READ COMMITTED)

只读取提交的数据并等待其他事务释放排他锁。这种隔离级别,读数据的共享锁在读操作完成后立即释放,因此会出现不可重复读问题。

(3)可重复读(REPEATABLE READ)

像读已提交级别那样读数据,但会保持共享锁直到事务结束。这种隔离级别,虽然保持行锁到事务提交结束,但是不能阻碍其他事务插入数据,所以存在幻读问题。

(4)可串行化(SERIALIZABLE)

工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围。

下表是是各隔离级别对各种异常的控制能力:

  第一类更新丢失可能性 脏读可能性 不可重复读可能性 第二类丢失更新可能性 幻读可能性
读未提交 N Y Y Y Y
读已提交 N N Y Y Y
可重复读 N N N N Y
串行读 N N N N N
 

以上是关于事务隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

Spring事务隔离级别:REQUIRES_NEW使用细节

MySQL的默认事务隔离级别是?

如何更改mysql事务隔离级别

mysql 的事务隔离级别 及各个隔离级别应用场景,详细

MySQL-8事务与隔离级别IO

数据库事务隔离级别 一般用哪个