三、事务的特性与隔离级别(MySQL)

1、事务的特性

数据库的事务可以简单理解为是一组SQL语句。对于事务内的SQL语句,要么全部执行成功,要么全部执行失败。事务具有四大特性(ACID):

原子性(Atomicity)

一个事务是一个不可分割的最小工作单元,事务包含的所有操作要么全部提交成功,要么全部失败回滚。

一致性(Consistency)

数据库总是从一个一致性状态转换到另一个一致性状态。

拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,也就是说,数据总是从一个一致性状态转换到另一个一致性状态,这就是事务的一致性。

隔离性(Isolation)

事务的隔离性指的是,当并发产生多个事务时,各个事务相互之间相互隔离、互不影响。对于隔离性来说,数据库可以设置不同的隔离级别,隔离级别越高,事务之间的相互影响程度就越低,但是并发程度也会随之下降。

持久性(Durability)

一旦事务提交,所做的修改就会永久保存在数据库中。

2、事务的隔离级别

在了解事务的隔离级别之前,首先了解一下在并发事务中存在的问题:

脏读

事务A读取了事务B已修改但未提交的数据。

不可重复读

事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

幻读

例如,事务A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是事务B就在这个时候插入了一条具体分数的记录,当事务A修改结束值后,再次查询发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

不可重复读与幻读的区别

不可重复读是由对某一行数据的修改引起的,侧重于对表中已有数据的修改(update);幻读是增加或者删除了某行引起的,重于对往表中新插入或者删除一条记录(insert/delete)。因此,解决不可重复读的问题通常只需对某一行加锁,解决幻读需要对整张表加锁。

了解了事务中存在的并发问题之后,再来看下事务的隔离级别。在SQL标准定义了4种隔离级别,分别如下:

读未提交(Read Uncommitted)

事务中的修改,即使没有提交,对其他事务也是可见的。换句话说,在一个事务里面,能够读取到其他事务尚未提交的数据,未提交的数据称为脏数据,这种情况就叫做脏读。

读未提交的隔离级别最低,存在脏读问题,相当于啥也没干,事务之间没有隔离,不能解决什么问题。而且效率并不比其他级别好太多,因此实际应用非常少。

读已提交(Read Committed)

一个事务开始时,只能看到其他已经提交的事务所做的修改,对其他事务未提交的修改是看不见的。换言之,一个事务所做的修改在最终提交之前,对其他事务是不可见的。这就解决了前面的脏读问题,它满足隔离性简单的定义,是多数数据库默认的隔离级别(但不是MySQL的默认隔离级别)。

但是,读已提交不能解决不可重复读的问题。也就是说,在一个事务A中,首先读取了一条数据,然后事务B对这条数据进行了修改,并且进行了提交,然后在事务A中再次读取此数据时,就会发现与之前读取的值不同,这就是不可重复读。

可重复读(Repeatable Read)

解决了脏读和不可重复读问题,是MySQL的默认隔离级别。在可重复读隔离级别中,能够保证在同一个事务中,多次读取同一行记录时,读取到的结果是相同的。但是,如果在一个事务中,多次读取的不是某一行记录的值,而是总的行数之类的数据,就有可能出现幻读问题。比如事务A首先读取了表中总的行数,然后事务B往表中新插入或者删除了一条数据并进行了事务提交,然后在事务A中再次读取表中总的行数,就会发现与之前读取的结果不同,这种情况就叫做幻读。

串行化(Serializable)

强制事务的串行执行,避免了包括幻读在内的一切并发问题,但是效率太低。

事务的隔离级别与对应存在的并发问题总结如下(MySQL):

事务隔离级别

存在脏读问题

存在不可重复读问题

存在幻读问题

未提交读

(read-uncommitted)

不可重复读

(read-committed)

可重复读

(repeatable-read)

串行化(serializable)

补充:

1、事务隔离级别为读提交时,写数据只会锁住相应的行

2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。

3、事务隔离级别为串行化时,读写数据都会锁住整张表

4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

3、MySQL验证事务的隔离级别

MySQL中默认隔离级别为:可重复读。

3.1、验证读未提交级别

 首先打开一个MySQL窗口,设置隔离级别为读未提交,开启一个事务,查看表中数据,此时count=50

 

 再打开一个客户端,设置隔离级别为读未提交,开启一个事务,将count修改为45,并且不提交事务

 此时再到客户端A中查看,可以看到读取到了事务B尚未提交修改的值:count=45

3.2、验证读已提交级别

 首先打开一个MySQL窗口A,设置隔离级别为读已提交,开启一个事务,查看表中数据,此时count=50

 

 再打开一个客户端,设置隔离级别为读已提交,开启一个事务,将count修改为45,并且不提交事务

再次在窗口A中的事务中 查看数据,读取到的count仍然为50,不会读取到事务B未提交的脏数据。

 

 在窗口B中提交事务

再次在窗口A中的事务查看,读取到了事务B已经提交的数据count=45,在同一个事务中多次读取同一个数据,得到了不同的结果,出现了不可重复读的问题。

3.3、验证可重复读级别

3.4、验证串行化级别

猜你喜欢

转载自blog.csdn.net/sun_lm/article/details/120191357