什么是脏读不可重复读幻读?一文带你搞定MySQL事务隔离级别

Posted c.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是脏读不可重复读幻读?一文带你搞定MySQL事务隔离级别相关的知识,希望对你有一定的参考价值。

什么是脏读、不可重复读、幻读?一文带你搞定mysql事务隔离级别

事务

既然要讲事务隔离级别,那我们肯定要来先说一下事务这个概念。

那什么是事务呢?

我们可以看到oracle的解释是:

A transaction is a logical, atomic unit of work that contains one or more SQL statements.

参考:https://docs.oracle.com/database/121/CNCPT/transact.htm#CNCPT117

我们一提到事务就会想到ACID这四个特性,但事务的定义本身并不具备这四个特性。事务只是一个改变,是一些的操作集合;用专业的术语去解释,就是一个程序的执行单元,只是保证了这个执行单元中的SQL要么都执行要么都撤销;我们需要通过某些手段,尽可能让这个执行单元满足这四个特性,那么,我们就可以称它是一个事务,或者说是一个正确的,完美的事务。(这里先举个栗子,比如在读未提交的隔离级别下,它不满足隔离性,但是它确实是一个事务,最完美的事务应该就是串行化隔离级别的事务了,这是对事务的隔离特性的完美实现,也是事务隔离特性的要求)

ACID四个特性

原子性(Atomicity)

All tasks of a transaction are performed or none of them are. There are no partial transactions. For example, if a transaction starts updating 100 rows, but the system fails after 20 updates, then the database rolls back the changes to these 20 rows.

满足原子操作单元,对数据的操作,要么全部执行,要么全部不执行。

一致性(Consistency)

The transaction takes the database from one consistent state to another consistent state. For example, in a banking transaction that debits a savings account and credits a checking account, a failure must not cause the database to credit only one account, which would lead to inconsistent data.

事务应确保数据库的状态从一个一致状态转变为另一个一致状态。即事务开始和完成时,数据都必须保持一致。

隔离性(Isolation)

The effect of a transaction is not visible to other transactions until the transaction is committed. For example, one user updating the hr.employees table does not see the uncommitted changes to employees made concurrently by another user. Thus, it appears to users as if transactions are executing serially.

多个事务并发执行时,一个事务的执行不应影响其他事务的执行。事务之间相互独立,中间状态对外部不可见。

持久性(Durability)

Changes made by committed transactions are permanent. After a transaction completes, the database ensures through its recovery mechanisms that changes from the transaction are not lost.

已被提交的事务对数据库的修改应该永久保存在数据库中。数据的修改是永久性的,即使系统出现任何故障都能够保持。

事务隔离级别

前面提到了可串行化是最完美的隔离级别,但是串行的并发能力是最差的,
但是如果提供并发能力(破坏隔离性)往往会破坏数据的一致性,因此需要在并发量和一致性之间找到一个平衡。最后大佬们根据对数据一致性破坏的程度将事务的隔离性区分了4个级别,一致性破坏造成的影响区分为: 脏读、幻读、不可重复读,这就是ANSI SQL-92标准

下面就讲一下提高并发能力,在多个事务并发执行的时候会引发哪些问题?

多个事务并发执行时引发的问题

一般情况下,多个单元操作(事务,这里的事务,并不是完美的事务,文章最开始提到的)并发执行,会出现这么几个问题。

脏读

脏读:A事务还未提交,B事务就读到了A操作的结果。(破坏了隔离性)

1、在事务A执行过程中,事务A对数据资源进行了修改,事务B读取了事务A修改后的数据。

2、由于某些原因,事务A并没有完成提交,发生了RollBack操作,则事务B读取的数据就是脏数据。

这种读取到另一个事务未提交的数据的现象就是脏读(Dirty Read)。

参考:https://www.zhihu.com/question/458275373

下面就来演示一下什么事脏读,这里涉及到事务的隔离级别,后面会讲,这里可以先不用太关心。

首先我们创建一个表,还有插入一条数据。

create table test_tb(
     id varchar(60) primary key  ,
     name varchar(30)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into test_tb value (1,'Kevin');

然后我们打开一个新的窗口, 我们查看当前窗口的事务隔离级别

SELECT @@GLOBAL.tx_isolation, @@tx_isolation; -- MYSQL8之前

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation; -- MYSQL8之后

可以看到我们的隔离级别是REPEATABLE-READ

然后我们把当前的窗口,也就是当前的session的隔离级别改成READ-UNCOMMITTED

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 修改当前session的隔离级别为读未提交

然后我们再看看当前session的事务隔离级别

现在我们的准备工作做好了,我们再打开一个新的窗口,这个窗口的隔离级别是REPEATABLE-READ

首先我们在REPEATABLE-READ这个窗口开启一个事务,然后执行一个update操作,但是我们不提交数据。

-- REPEATABLE-READ
start transaction ;
update test_tb set name = 'KEVIN' where id = 1;

正常来说,如果我们再打开一个REPEATABLE-READ的新窗口,这个时候是读不到未提交的update后的数据。只有执行update操作的那个事务里面才能读取到。

但是在READ-UNCOMMITTED级别下,是能够读取到未提交的数据。

所以这个时候就出现了脏读,读取到了未提交的数据,这个很显然就是破坏了ACID四个特性中的隔离性了。

不可重复读

不可重复读:A事务在本次事务中,对自己未操作过数据,进行多次读取,结果出现不一致或记录不存在的情况。(重点是update和delete)

事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。

这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读(Nonrepeatable Read)。

现在我们演示一下不可重复读的情况。

首先我们还是打开一个新的窗口,我们把当前的session的隔离级别改为读已提交,在读已提交的隔离级别下是不会出现脏读的,可以参考上面脏读的例子去试一试,这里就不展示了。但是在读已提交的隔离级别下,还是会出现不可重复读的情况。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED ; -- 修改当前session的隔离级别为读已提交

然后我们开启事务并且查看数据。

start transaction ;
select * from test_tb; -- READ-COMMITTED

然后我们打开另一个新的窗口,这个窗口依然还是REPEATABLE-READ,然后我们修改数据并提交,这里跟脏读的区别是,我们开启了事务并且进行了提交。

-- REPEATABLE-READ
start transaction ;
update test_tb set name = 'KEVIN' where id = 1;
commit;

然后我们回到我们之前READ-COMMITTED的窗口,在事务中继续查看数据,然后我们发现我们两次读取的数据不一样了,第一次读取到的是Kevin,第二次读取到的是KEVIN

所以这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读。

然后我们再来演示一下delete的情况。

首先我们有两条数据。

然后我们在读已提交的窗口中开启事务并查看数据。

start transaction ;
select * from test_tb; -- READ COMMITTED
select count(*) from test_tb; -- READ COMMITTED

然后我们在另一个新窗口,我们开启事务并删除数据,最后提交。

-- REPEATABLE-READ
start transaction ;
delete from test_tb where name = 'Kevin';
commit;

最后我们回到读已提交的窗口,继续查看数据,会发现数据条数变少了。

所以如果事务A 按一定条件搜索, 期间事务B 删除了符合条件的某一条数据,导致事务A 再次读取时数据少了一条。这种情况归为 不可重复读

幻读

幻读:A事务在本次事务中,先读取了一遍数据,发现数据不存在,过了一会,又读取了一遍,发现又有数据了。(破坏了一致性,重点是insert)

我们来演示一下幻读的情况,首先我们还是打开窗口,设置当前的隔离级别为REPEATABLE-READ

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ; -- 修改当前session的隔离级别为可重复读

在可重复读的隔离级别下是不会出现不可重复读的情况,可以参照上面不可重复读的例子操作一下,试一试update(读取的还是原来的数据)和delete(读取的条数没有减少)的情况,这里就不演示了。

首先我们DB里面一样只有一条数据。

我们在REPEATABLE-READ的窗口中开启一个事务,然后查看数据,确实是一条数据

start transaction ;
select * from test_tb; -- REPEATABLE-READ
select count(*) from test_tb; -- REPEATABLE-READ

然后我们开启一个新的窗口,默认也是REPEATABLE-READ的隔离级别。

然后我们开启事务并插入一条数据

-- REPEATABLE-READ
start transaction ;
insert into test_tb value (2,'Kevin');
select count(*) from test_tb;
commit;

可以看到现在是两条数据了。

然后我们回到之前的窗口,也就是最开始还没提交事务的窗口。这个时候我们查看一下数量。

可以发现我们的数据条数还是一条,但是我们这个时候执行一个update语句。

update test_tb set name = 'KEVIN' where name = 'Kevin';
start transaction ;
select * from test_tb; -- REPEATABLE READ
select count(*) from test_tb; -- REPEATABLE READ
update test_tb set name = 'KEVIN' where name = 'Kevin';

可以发现执行的时候却是影响了两条记录,就好像出现了幻觉一样,所以这个现象就是幻读

MySQL四个事务隔离级别

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)-
可重复读(repeatable-read)--
串行化(serializable)---

参考

https://docs.oracle.com/database/121/CNCPT/transact.htm#CNCPT117

什么是脏读、不可重复读、幻读?

搞懂MySQL对事务的破坏与补救措施,扫清对事务的误区

https://github.com/hongwen1993/all/blob/master/database/Isolation.md

【MySQL】事务与隔离级别(99%的人存在误区)

直接写SQL给你演示什么是脏读 幻读 可重复读,思路清晰

四个案例看懂 MySQL 事务隔离级别

ANSI SQL-92标准

面试官一上来就问Mysql:幻读到底是什么?

以上是关于什么是脏读不可重复读幻读?一文带你搞定MySQL事务隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

什么是脏读,幻读和不可重复读

什么是脏读,不可重复读,幻读

数据库 - 脏读不可重复读幻读。

Mysql的事务是什么?什么是脏读?幻读?不可重复读?

MySQL理论:脏读不可重复读幻读

MySQL理论:脏读不可重复读幻读