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

1.引言


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

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

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

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

备注:不可重复读对应的是修改,即update操作。幻读对应的是插入操作。幻读是不可重复读的一种特殊场景。

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

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

2.事务隔离级别


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


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

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

参考

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

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

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

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

注意:处于该隔离级别的事务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,能够拿到数据。

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

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

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

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

快照读读取的是快照数据。不加锁的简单的 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,可以避免脏读,不可重复读,不可避免幻读 

你可能感兴趣的:(数据库,java,开发语言)