数据库基础-mvcc-对数据库事务的四种隔离级别逐步加深的理解

文章介绍了数据库事务的四种隔离级别——ReadUncommitted、ReadCommitted、RepeatableRead和Serializable,以及它们如何解决并发执行时可能出现的脏读、不可重复读和幻读问题。每种隔离级别都有其优缺点,如ReadUncommitted允许脏读,而Serializable虽然避免所有读现象但影响并发性能。文章通过实例解释了这些概念,并提到了MySQL的默认隔离级别及其查询与修改方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.引言


数据库事务的隔离级别有4种,由低到高分别为Read uncommitted(读未提交) 、Read committed (读已提交)、Repeatable read (重复读)、Serializable (序列化)。读现象是在多个事务并发执行时,在读取数据方面可能碰到的问题。包括脏读、不可重复读、幻读。

脏读

        读到了脏数据,即无效数据。

情景:脏读指事务A读取到了事务B更新了但是未提交的数据,然后事务B由于某种错误发生回滚,那么事务A读取到的就是脏数据。

结论:读取未提交的数据!

不可重复读

是指在数据库访问中,一个事务内的多次相同查询却返回了不同数据。

情景:比如说事务A的执行周期较长,事务A在第一次读取某个数据时,此数据的值为100,读取完成后,事务A又去执行其他的事情,在这个过程中,事务B将这个数据的值修改为200,然后事务A做完其他的事情后,又来读取这个数据的值,发现这个值和第一次所读取的值不相同,这种现象称为不可重复读。

结论:前后多次读取,数据内容不一致!

幻读

指同一个事务内多次查询返回的结果集不一样,比如增加了行记录。

幻读指的是事务A在查询完记录总数后,事务B执行了新增数据的操作,事务A再次查询记录总数,发现两次查询的结果不一致,平白无故的多了几条记录,这种现象称为幻读。

不可重复度和幻读的区别

幻读和不可重复读的本质是一样的,两者都表现为两次读取的结果不一致。但是不可重复读指的是两次读取同一条记录的值不同,而幻读指的是两次读取的记录数量不同。

要想解决脏读、不可重复读、幻读等读现象,那么就需要提高事务的隔离级别。但是随之带来的,隔离级别越高,并发能力越低。所以,需要根据业务去进行衡量,具体场景应该使用哪种隔离级别。

下面通过事例一一阐述它们的概念与联系。

2.事务隔离级别


2.1 Read uncommitted(读未提交,导致脏读问题)


提供了事务建最小限度的隔离。理解:就是一个事务可以读取另一个未提交事务的数据。所以这是脏数据。

要去想这种隔离级别会出现什么问题?

参考

mysql 读取未提交(脏读问题)与读取已提交(不可重复读问题)_读未提交_小洪帽i的博客-优快云博客

示例:小明去商店买衣服,付款的时候,小明正常付款,钱已经打到商店老板账户,但是小明发起的事务还没有提交。就在这时,商店老板查看自己账户,发现钱已到账,于是小明正常离开。小明在走出商店后,马上回滚差点提交的事务,撤销了本次交易曹邹。

结果:小明未付钱买到了衣服,商店老板实际未收到小明的付款。

分析:商店老板查看自己的资金账户,这个时候看到的是小明还没有提交事务的付款。这就是脏读。

注意:处于该隔离级别的事务A与B,如果事务A使用事务B不提交的变化作为计算的基础,然后哪些未提交的变化被事务A撤销,这就导致了大量的数据错误变化。

2.2 Read committed (读已提交,有不可重复读问题)

理解:A事务修改数据,同一时间B事务前后读取的数据不一致问题。

既然读未提交没办法解决脏数据问题,那么就有了读提交。读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据。那脏数据问题就迎刃而解了。

读提交事务隔离级别是大多数流行数据库的默认事务隔离级别,比如:Oracle,但不是 MySQL 的默认隔离级别。

我们来做一下验证,首先把事务隔离级别改为读提交级别:

设置完成后,需要重新打开 session 窗口,也就是新的 shell 窗口才可以。

同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id = 1 的记录行 age 字段改为30。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是14,直到事务A提交,此时在事务B中查询,发现 age 的值已经是 30 了。这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的(针对数据的修改),事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读取已提交隔离级别。

分析:该隔离级别可以解决脏读问题,却有不可重复读。

2.3 Repeatable read (重复读,有幻读(Phantom Read)的问题)

什么是幻读?

幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。 在可重复读隔离级别下,普通的查询是快照读,是不会看到 别的事务插入的数据的(针对数据的新增)。因此,幻读在“当前读”下才会出现。

在更新update语句之后,再次执行查询sql,能够拿到数据。

可重复读隔离下为什么会产生幻读?

为什么会有快照读和当前读??

在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在 当前读 下才会出现。

什么是快照读,什么是当前读?

快照读读取的是快照数据。不加锁的简单的 SELECT都属于快照读,比如这样:

SELECT * FROM player WHERE ...

当前读就是读取最新数据,而不是历史版本的数据。加锁的 SELECT,或者对数据进行增删改都会进行当前读。这有点像是 Java 中的 volatile 关键字,被 volatile 修饰的变量,进行修改时,JVM 会强制将其写回内存,而不是放在 CPU 缓存中,进行读取时,JVM 会强制从内存读取,而不是放在 CPU 缓存中。这样就能保证其可见行,保证每次读取到的都是最新的值。如果没有用 volatile 关键字修饰,变量的值可能会被放在 CPU 缓存中,这就导致读取到的值可能是某次修改的值,不能保证是最新的值。

说多了,我们继续来看,如下的操作都会进行 当前读。

SELECT * FROM player LOCK IN SHARE MODE;
SELECT * FROM player FOR UPDATE;
INSERT INTO player values ...
DELETE FROM player WHERE ...
UPDATE player SET ...

2.4 Serializable (序列化)


数据库事务的最高隔离级别。在此级别下,事务串行执行。可以避免脏读、不可重复读、幻读等读现象。但是效率低下,耗费数据库性能,不推荐使用。

3.mysql事务隔离级别查询


3.1mysql查看数据库实例默认的全局隔离级别sql


Mysql8以前:SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

Mysql8开始:SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

3.2修改隔离级别命令


SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

建议开发者在修改时,仅修改当前session隔离级别即可。

3.3mysql默认隔离级别


REPEATABLE-READ,可以避免脏读,不可重复读,不可避免幻读 

解决办法: 更改数据库的隔离级别,和具体情况具体分析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值