什么是脏读不可重复读幻读?一文带你搞定mysql事务隔离级别(代码片段)

c. c.     2023-01-03     183

关键词:

什么是脏读、不可重复读、幻读?一文带你搞定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理论:脏读不可重复读幻读(代码片段)

文章目录1.脏读(dirtyread)脏读是指事务读取到其他事务未提交的数据2.不可重复读(non-repeatableread)不可重复读是指在同一次事务中前后查询不一致的问题3.幻读(phantomread)幻读是一次事务中前后数据量发生变化,用户产生不可预... 查看详情

mysql理论:脏读不可重复读幻读(代码片段)

文章目录1.脏读(dirtyread)脏读是指事务读取到其他事务未提交的数据2.不可重复读(non-repeatableread)不可重复读是指在同一次事务中前后查询不一致的问题3.幻读(phantomread)幻读是一次事务中前后数据量发生变化,用户产生不可预... 查看详情

脏读不可重复读幻读

脏读(DirtyRead)    脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚  不可重复读(UnrepeatableRead)     不可重复读意味着,在数据库访问中,一个事务范围内两个... 查看详情

脏读不可重复读幻读

脏读:一个事务读取到了另外一个事务没有提交的数据         事务1:更新一条数据                  &nbs 查看详情

mysql锁与脏读不可重复读幻读详解(代码片段)

最近,在一次mysql死锁的生产事故中,我发现,关于mysql的锁、事务等等,我所知道的东西太碎了,所以,我试着用几个例子将它们串起来。具体做法就是通过不断地问问题、回答问题,再加上“适当”... 查看详情

五分钟了解mysql脏读幻读不可重复读mvcc(代码片段)

点击上方关注“终端研发部”设为“星标”,和你一起掌握更多数据库知识首先对多事务并发的问题的思考对innodb引擎执行流程 和bufferpool足够了解的话,那一定知道mysql系统在初始化的时候bufferpool会将内存分为多个缓... 查看详情

脏读不可重复读幻读

 1.脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。2.不可重复读:是指在一个事务内,多次读同一数据... 查看详情

数据库事务隔离级别--脏读幻读不可重复读

...peatableread 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。√:可能出现    ×:不会出现&nbs 查看详情

关于脏读幻读和不可重复读

1.脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 e.g.        1.Mary的原工资... 查看详情

何为脏读不可重复读幻读(代码片段)

2.0前言事务的隔离性是指多个事务并发执行的时候相互之间不受到彼此的干扰的特性,隔离性是事务ACID特性中的I,根据隔离程度从低到高分为ReadUncommitted(读未提交),ReadCommitted(读已提交),RepeatableRead(可重复读),Seriali... 查看详情

数据库的脏读幻读不可重复读

1.脏读:指一个事务A正在访问数据,并且对该数据进行了修改,但是这种修改还没有提交到数据库中(也可能因为某些原因Rollback了)。这时候另外一个事务B也访问这个数据,然后使用了这个被A修改的数据,那么这个数据就是... 查看详情

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

...,如果此时事务B数据回滚,事务A仍然是使用旧的数据即是脏的数据。顺序事务A事务B1开始事务2开始事务3查询余额2000元4取出1000元剩余1000元5查询余额剩余1000元(脏读)6产生异常事务发生回滚,余额剩余2000元7转入2000元,余额为3000... 查看详情

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

1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。2. 不可重复读 :是指在一个事务内,多... 查看详情

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

参考技术A1.脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。2.不可重复读:是指在一个事务内,多次读同一... 查看详情

mysql性能调优08_事务及其acid属性脏读不可重复读幻读隔离级别行锁表锁读锁写锁间隙锁临键锁(代码片段)

文章目录①.事务及其ACID属性②.脏读、不可重复读、幻读③.隔离级别④.锁分类表索、行锁、读锁、写锁⑤.表锁⑥.行锁⑦.间隙锁(GapLock)⑧.临键锁(Next-keyLocks)①.事务及其ACID属性事务:一个或一组sql语句组成一个执行单元,这个执... 查看详情

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

这三者都是数据库事务的错误情况。1、脏读:事务A读到了事务B未提交的数据。2、不可重复读:事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到row1,但列内容发生了变化。3、幻读:事务A第一次查询... 查看详情

数据库脏读不可重读读幻读

脏读:脏读是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。也就是说,当前事务读到的数据是别的事务想要修改成为的但是没有修... 查看详情

数据库并发事务存在的三个问题(脏读不可重复读幻读)

如果不考虑事务隔离性,可能会发生以下情况脏读:事务a,读到了事务b未提交的数据,如果事务a读到了事务b的一些中间数据,待处理的数据。b事务数据还没有提交,就被a事务访问了(解决方法:将读未提交级别提高到读已提... 查看详情