数据库事务的隔离级别有4种,由低到高分别为Read uncommitted(读未提交) 、Read committed (读已提交)、Repeatable read (重复读)、Serializable (序列化)。读现象是在多个事务并发执行时,在读取数据方面可能碰到的问题。包括脏读、不可重复读、幻读。
脏读:读到了脏数据,即无效数据。
不可重复读:是指在数据库访问中,一个事务内的多次相同查询却返回了不同数据。
幻读:指同一个事务内多次查询返回的结果集不一样,比如增加了行记录。
备注:不可重复读对应的是修改,即update操作。幻读对应的是插入操作。幻读是不可重复读的一种特殊场景。
要想解决脏读、不可重复读、幻读等读现象,那么就需要提高事务的隔离级别。但是随之带来的,隔离级别越高,并发能力越低。所以,需要根据业务去进行衡量,具体场景应该使用哪种隔离级别。
下面通过事例一一阐述它们的概念与联系。
提供了事务建最小限度的隔离。理解:就是一个事务可以读取另一个未提交事务的数据。所以这是脏数据。
要去想这种隔离级别会出现什么问题?
参考
mysql 读取未提交(脏读问题)与读取已提交(不可重复读问题)_读未提交_小洪帽i的博客-CSDN博客
示例:小明去商店买衣服,付款的时候,小明正常付款,钱已经打到商店老板账户,但是小明发起的事务还没有提交。就在这时,商店老板查看自己账户,发现钱已到账,于是小明正常离开。小明在走出商店后,马上回滚差点提交的事务,撤销了本次交易曹邹。
结果:小明未付钱买到了衣服,商店老板实际未收到小明的付款。
分析:商店老板查看自己的资金账户,这个时候看到的是小明还没有提交事务的付款。这就是脏读。
注意:处于该隔离级别的事务A与B,如果事务A使用事务B不提交的变化作为计算的基础,然后哪些未提交的变化被事务A撤销,这就导致了大量的数据错误变化。
理解: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的查询结果,这就是不可重复读,也就是读取已提交隔离级别。
分析:该隔离级别可以解决脏读问题,却有不可重复读。
什么是幻读?
“幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。 在可重复读隔离级别下,普通的查询是快照读,是不会看到 别的事务插入的数据的(针对数据的新增)。因此,幻读在“当前读”下才会出现。
在更新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 (序列化)
数据库事务的最高隔离级别。在此级别下,事务串行执行。可以避免脏读、不可重复读、幻读等读现象。但是效率低下,耗费数据库性能,不推荐使用。
Mysql8以前:SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
Mysql8开始:SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
建议开发者在修改时,仅修改当前session隔离级别即可。
REPEATABLE-READ,可以避免脏读,不可重复读,不可避免幻读