快速理解脏读,不可重复读,幻读

article/2025/10/2 22:50:11

介绍

要聊事务,不可避免的要提到数据库事务的四大特性:ACID

  • atomic
  • consistence
  • isolation
  • durability

先放一个表格,看看4个隔离级别会出现的各种问题,网上的解释一大堆。看完后还是一脸懵逼,感觉懂了,又好像没懂。因为没有具体的演示例子,索性自己尝试复现这几个问题。

√ 为会发生,×为不会发生:

隔离级别脏读不可重复读幻读
read uncommitted(未提交读)
read committed(提交读)×
repeatable read(可重复读)××
serialization(可串行化)×××

再总结mysql 的常用命令(下面会用到):

命令含义
select version()查看MySQL版本
SELECT @@tx_isolation查看MySQL隔离级别
set session transaction isolation level 隔离级别会话层面设置隔离级别
start transaction开启事务
commit提交事务
rollback回滚事务

演示

首先建立如下表:

CREATE TABLE `account` (`id` int(2) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT NULL,`balance` int(3) DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

用Navicat(其他工具也行)开2个查询的tab页,表示2个会话
在这里插入图片描述

首先,在2个Tab页面分别执行SELECT @@tx_isolation,输出都是REPEATABLE-READ,表明MySQL默认隔离级别为REPEATABLE-READ(可重复)读。

*脏读

表中的数据如下,设置隔离级别为未提交读
在这里插入图片描述

时间客户端A客户端B
T1set session transaction isolation level read uncommitted;
start transaction(开启事务);
update account set balance = balance+1000 where id = 1;
select * from account where id = 1;
设置为未提交读,给张三账号+1000,输出为2000
T2set session transaction isolation level read uncommitted;
start transaction;
select * from account where id = 1;
查询余额输出为2000
T3rollback
T4commit
T5select * from account where id = 1;
查询余额输出为1000

----->举个例子概述一下这个过程,财务给张三发了1000元的工资,然后张三查询自己的账户,果然多了1000元,变成了2000元,结果财务操作过程有误,事务回滚。当张三再查账户时,却发现账户只有1000元。

脏读就是指当事务A对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务B也访问这个数据,然后使用了这个数据。

----->再举一例,表中的数据如下:
在这里插入图片描述

时间客户端A客户端B
T1set session transaction isolation level read uncommitted;
start transaction;
update account set balance = balance-1000 where id = 1;
update account set balance = balance+1000 where id = 2;
T2set session transaction isolation level read uncommitted;
start transaction;
select balance from account where id = 2;
update account set balance = balance -1000 where id = 2;
更新语句被阻塞
T3rollback
T4commit

执行完成,数据库中的数据如下
在这里插入图片描述
解释如下:

时间解释
T11给2转账1000
T22的余额够1000,购买1000元商品,更新语句被阻塞
T31回滚,1的余额变成1000,2的余额变成0
T42成功扣款,余额为0-1000=-1000

如此便出故障了!

*不可重复读

表中的数据如下,设置隔离级别为提交读
在这里插入图片描述

时间客户端A客户端B
T1set session transaction isolation level read committed;
start transaction;
select * from account where id = 2;
查询出余额输出为0;
T2set session transaction isolation level read committed;
start transaction;
update account set balance = balance + 1000 where id = 2;
select * from account where id = 2;
commit;
查询余额输出1000
T3select * from account where id = 2;
commit;
查询余额输出1000

不可重复读是指在事务1内,读取了一个数据,事务1还没有结束时,事务2也访问了这个数据,修改了这个数据,并提交。紧接着,事务1又读这个数据。由于事务2的修改,那么事务1两次读到的的数据可能是不一样的,因此称为是不可重复读。

当然你可以在T2时间段客户端B修改完id=2的账户余额但没有commit的时候,在客户端A查询id=2的账户余额,发现账户余额为0,可以证明提交读这个隔离级别不会发生脏读

现在用上面的例子看一下可重复读是个什么过程?

表中的数据如下,设置隔离级别为可重复读
在这里插入图片描述

时间客户端A客户端B
T1set session transaction isolation level repeatable read;
start transaction;
select * from account where id = 2;
查询余额输出为0;
T2set session transaction isolation level repeatable read;
start transaction;
update account set balance = balance + 1000 where id = 2;
select * from account where id = 2;
commit;
查询余额输出1000
T3select * from account where id = 2;
commit;
查询余额输出0

仔细看这个例子和上面的例子在T3时间段的输出,理解了什么叫可重复读了吧?当我们将当前会话的隔离级别设置为可重复读的时候,当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。
----------但是在可重复读的隔离级别上,会产生幻读的问题。

*幻读

表中的数据如下,设置隔离级别为可重复读
在这里插入图片描述
先上一段《高性能MySQL》对于幻读的解释

所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。

用大白话解释一下,就是事务1查询id<10的记录时,返回了2条记录,接着事务2插入了一条id为3的记录,并提交。接着事务1查询id<10的记录时,返回了3条记录,说好的可重复读呢?结果却多了一条数据。

MySQL通过MVCC解决了这种情况下的幻读,我们可以验证一下

时间客户端A客户端B
T1set session transaction isolation level repeatable read;
start transaction;
select count(*) from account where id <=10;
输出2;
T2set session transaction isolation level repeatable read;
start transaction;
insert into account(id,name,balance) values(“3”,“王五”,“0”) ;
select count(*) from account where id <=10;
commit;
输出3
T3select count(*) from account where id <=10;
commit;
输出2

这种情况下的幻读被解决了,再举一例,表中的数据如下
在这里插入图片描述

时间客户端A客户端B
T1set session transaction isolation level repeatable read;
start transaction;
select count(*) from account where id =3;
输出0;
T2set session transaction isolation level repeatable read;
start transaction;
insert into account(id,name,balance) values(“3”,“王五”,“0”) ;
commit;
T3insert into account(id,name,balance) values(“3”,“王五”,“0”);
;主键重复,插入失败
T4select count(*) from account where id =3;
输出0;
T5rollback;

select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,这个就有问题了。

很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

注意:不可重复读和幻读的区别是:前者是指读到了已经提交的事务的更改数据(修改或删除),后者是指读到了其他已经提交事务的新增数据
对于这两种问题解决采用不同的办法,防止读到更改数据,只需对操作的数据添加行级锁,防止操作中的数据发生变化;而防止读到新增数据,往往需要添加表级锁,将整张表锁定,防止新增数据(oracle采用多版本数据的方式实现)。

当隔离级别设置为可串行化,强制事务串行执行,避免了前面说的幻读的问题。


http://chatgpt.dhexx.cn/article/1SQi482Z.shtml

相关文章

MySQL之脏写、脏读、不可重复读、幻读

脏写和脏读都是在多个事务同时修改或读取同一行数据的情况下产生的问题。比如现在有事务1和事务2同时对一行数据进行修改&#xff0c;事务1将值改成1&#xff0c;而事务2将值改成了2&#xff0c;这时的值应该是2&#xff0c;但是就在这时&#xff0c;事务1发生了回滚&#xff0…

数据库必备知识:脏读和幻读的定义及应对策略

随着数据库应用的广泛使用&#xff0c;数据库并发性和一致性的问题成为了引起重视的问题之一。其中&#xff0c;脏读&#xff08;Dirty Read&#xff09;和幻读&#xff08;Phantom Read&#xff09;是常见的并发访问问题&#xff0c;本文将对脏读、幻读进行详细介绍&#xff0…

Seata AT模式怎样防止脏写和脏读

前言 Seata AT 模式是一种非侵入式的分布式事务解决方案&#xff0c;Seata 在内部做了对数据库操作的代理层&#xff0c;我们使用 Seata AT 模式时&#xff0c;实际上用的是 Seata 自带的数据源代理 DataSourceProxy&#xff0c;Seata 在这层代理中加入了很多逻辑&#xff0c;…

mysql 脏数据是什么_什么是脏读?

什么是脏读&#xff1f; 脏读又称无效数据的读出&#xff0c;是指在数据库访问中&#xff0c;事务T1将某一值修改&#xff0c;然后事务T2读取该值&#xff0c;此后T1因为某种原因撤销对该值的修改&#xff0c;这就导致了T2所读取到的数据是无效的&#xff0c;值得注意的是&…

[Database] 脏读、幻读这些都是什么?事务隔离级别又是什么?MySQL数据库的事务隔离级别都有哪些?

文章目录 前言事务隔离级别三种数据不一致问题1. 脏读2. 不可重复读3. 幻读不可重复读 vs 幻读 四种事务隔离级别1. READ UNCOMMITTED2. READ COMMITTED3. REPEATABLE READ4. SERIALIZABLE 不同事务隔离级别会面临的问题不同隔离事务级别的使用率排名 实战查看事务隔离级别更改…

Mysql-详解脏读、不可重复读、幻读

Mysql的事务隔离级别 Mysql有四种事务隔离级别&#xff0c;这四种隔离级别代表当存在多个事务并发冲突时&#xff0c;可能出现的脏读、不可重复读、幻读的问题。 脏读 大家看一下&#xff0c;我们有两个事务&#xff0c;一个是 Transaction A&#xff0c;一个是 Transaction B…

MySQL的事务,脏读,不可重复读,幻读

一、什么是事务 在MySQL中&#xff0c;事务是一种机制、一个操作序列&#xff0c;是访问和更新数据库的程序执行单元。事务中包含一个或多个数据库操作命令&#xff0c;会把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#…

脏读、幻读、不可重复读,傻傻分不清楚

脏读 &#xff08;针对未提交数据) 脏读又称无效数据读出&#xff08;读出了脏数据&#xff09;。一个事务读取另外一个事务还没有提交的数据叫脏读。 例如&#xff1a;事务T1修改了某个表中的一行数据&#xff0c;但是还没有提交&#xff0c;这时候事务T2读取了被事务T1修改…

【MySQL理论】脏读、不可重复读、幻读

文章目录 1. 脏读(dirty read)脏读是指事务读取到其他事务未提交的数据 2. 不可重复读(non-repeatable read)不可重复读是指在同一次事务中前后查询不一致的问题 3. 幻读(phantom read)幻读是一次事务中前后数据量发生变化&#xff0c;用户产生不可预料的问题 4. 不可重复读和幻…

脏读、不可重复读、幻读、丢失更新

根儿上来说&#xff0c;为什么需要事务和锁&#xff1f; 如果所有的操作都是依次进行的&#xff0c;或者说mysql的server是单线程的&#xff0c;就不会有并发问题&#xff0c;也就不需要事务和锁了。然而事实上&#xff0c;是多客户端&#xff0c;多线程的&#xff0c;所有必须…

一文搞懂MySQL脏读,幻读和不可重复读

目录 MySQL 中事务的隔离 1.READ UNCOMMITTED2.READ COMMITTED3.REPEATABLE READ4.SERIALIZABLE前置知识 1.事务相关的常用命令2.MySQL 8 之前查询事务的隔离级别3.MySQL 8 之后查询事务的隔离级别4.查看连接的客户端详情5.查询连接客户端的数量6.设置客户端的事务隔离级别7.新…

脏读、幻读和不可重复读

一、引言 脏读、不可重复读和幻读是数据库中由于并发访问导致的数据读取问题。当多个事务同时进行时可以通过修改数据库事务的隔离级别来处理这三个问题。 二、问题解释 1、脏读&#xff08;读取未提交的数据&#xff09; 脏读又称无效数据的读出&#xff0c;是指在数据库访…

一文详解脏读、不可重复读、幻读

MySQL 是支持多事务并发执行的。否则来一个事务处理一个请求&#xff0c;处理一个人请求的时候&#xff0c;其它事务都等着&#xff0c;那估计都没人敢用MySQL作为数据库,因为用户体验太差&#xff0c;估计都要砸键盘了。 既然事务可以并发操作,这里就有一些问题&#xff1a;一…

快速理解脏读、不可重复读和幻读

MySQL的InnoDB引擎是支持事务的&#xff0c;但是并发事务的处理又会带来以下问题&#xff1a; 脏读不可重复读幻读 一、脏读 脏读指事务A读取到了事务B更新了但是未提交的数据&#xff0c;然后事务B由于某种错误发生回滚&#xff0c;那么事务A读取到的就是脏数据。 具体的说…

引用及指针和引用的区别

一.在C语言中&#xff0c;我们要给函数传参&#xff0c;有两种方法。 1.传值 void Swap(int a,int b) {int tmpa;ab;btmp; } int main() {int a10;int b20;Swap(a,b);return 0; }优点&#xff1a;形参不影响实参&#xff08;保护实参&#xff09;&#xff0c;可读性强。 缺点…

C++ | 指针和引用的区别

01. C——指针和引用的区别 指针是一个存储变量地址的变量&#xff0c;指向内存的一个存储单元。 引用只是一个别名。 int a1; int *p&a;int a1; int &ba;使用sizeof看一个指针的大小是8&#xff0c;而引用则是被引用对象的大小。指针可以被初始化为NULL&#xff0c;…

C++—指针与引用的区别与联系

一、为什么要有引用&#xff08;C语言没有&#xff09; ▪ 因为引⽤和值有⼀样的语义&#xff0c;而指针不是 ▪ 不存在空引⽤&#xff0c;必须初始化&#xff1b;保证值不变&#xff0c;保证编译器更加安全 ▪ 加减号、赋值操作符&#xff0c;作⽤在引用上会触发对象的操作符重…

C++指针与引用的区别

指针和引用的区别 ①指针是一个变量&#xff0c;存储一个成员的地址&#xff1b;引用是一个常量&#xff08;指针常量&#xff09;&#xff0c;相当于一个成员的别名 ②指针声明和定义可以分开&#xff1b;引用声明时必须初始化 int* a;//指针声明 anew int(1);//指针定义 int…

【C++】---指针和引用的区别

指针和引用的区别 两者本质两者区别两者的相同点为什么传引用比传指针更安全两者做返回值效率的比较 两者本质 引用是别名&#xff0c;指针是地址、实体 两者区别 不同点分析1.初始化要求不同引用在创建的同时必须初始化&#xff0c;即引用到一个有效的对象&#xff0c;而指…

【C++】指针和引用的区别及指针传递和引用传递的区别

一、指针和引用的区别&#xff1a; 1.指针是一个变量&#xff0c;只不过这个变量存储的是一个地址&#xff0c;指向内存的一个存储单元&#xff1b;引用和原来的变量实质上是同一个东西&#xff0c;只不过是原变量的一个别名。 2.指针的值在初始化后可变&#xff0c;即指向其…