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

article/2025/10/2 23:30:12

文章目录

  • 1. 脏读(dirty read)
    • 脏读是指事务读取到其他事务未提交的数据
  • 2. 不可重复读(non-repeatable read)
    • 不可重复读是指在同一次事务中前后查询不一致的问题
  • 3. 幻读(phantom read)
    • 幻读是一次事务中前后数据量发生变化,用户产生不可预料的问题
  • 4. 不可重复读和幻读的比较
  • 5. 总结
  • 6. 解决方法

1. 脏读(dirty read)

脏读是指事务读取到其他事务未提交的数据

例如:有事务A、B和一条记录:id为1,name为张三

B首先进行更新操作,将name的值由张三改为张老三,但还未提交事务

begin;
update stu set name = '张老三' where id = 1

然后A进行查询操作,查询姓名为张老三

select name from stu where id = 1

然后B由于某种原因被回滚,name自然从张老三恢复到了张三

A就会出现一个问题,读取的是张老三,但数据库中实际存储的是张三,我到底是用哪一个呢?

这就是脏读,事务A读取到事务B未提交的数据

脏读是我们在整个数据库操作中最普遍的一个现象,但是在日常开发中我们几乎不会遇到脏读,原因后面说

看图更容易理解
在这里插入图片描述

2. 不可重复读(non-repeatable read)

不可重复读是指在同一次事务中前后查询不一致的问题

例如:

A先查询了一条记录,name为张三

select name from stu where id = 1

然后B执行了更新,并且提交

begin;
update stu set name = '张老三' where id = 1;
commit

A再次按相同条件查询该记录,name为张老三

select name from stu where id = 1

A就会出现一个问题,同样的查询语句,两次的执行结果却不一致(不重复)

第二次查询得到的name应该还是预期的张三,因为很明确,A并没有对name进行修改,但是在并发环境下,假如两条select语句间有另外一个事务对name执行了update操作并提交了,把张三的名字改为了张老三,A再次执行同样查询导致name由张三变为张老三

A就有意见了,我明明没有对name进行修改,可是为什么后来变成张老三了?谁改我数据了?

这就是不可重复读:同一次事务中前后查询不一致

它会让我们的程序运行变得不可预期、不可控

看图更容易理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tJ2rTdRb-1631029938002)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20210907231354003.png)]

3. 幻读(phantom read)

幻读是一次事务中前后数据量发生变化,用户产生不可预料的问题

分为并发删除和并发插入的情况

1 并发插入

A先执行查询,假设当前只有一条数据,查询结果:id=1,name=张三

select * from stu

B插入了一条数据(已设置主键自增,省略id字段),name为李四

insert into stu(name) values('李四')

当A再次按相同条件查询

select * from stu

结果多了一条李四的记录(id=2,name=李四),刚才明明只有一条记录,怎么多出来一条?好像出现了幻觉

2 并发删除

将上面的插入操作换成删除操作,B删除了张三的记录

delete from stu where id = 1

结果A会发现张三的记录神秘的消失了,明明刚才还有的,怎么又没了,再次出现了幻觉

这就是幻读,看图更容易理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ma99aVHO-1631029938005)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20210907234039087.png)]

4. 不可重复读和幻读的比较

1 幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
2 不可重复读的重点是修改,幻读的重点在于插入或者删除
3 但如果从控制的角度来看, 两者的区别就比较大
对于前者, 只需要锁住满足条件的记录
对于后者, 要锁住满足条件及其相近的记录

5. 总结

脏读是指事务读取到其他事务正在处理的未提交数据

不可重复读指在并发更新时,另一个事务前后执行相同条件的查询得到的数据不一致

幻读指并发插入、删除时,另一个事务前后执行相同条件的查询得到的数据不一致

6. 解决方法

如何解决以上问题,事务的隔离级别就派上用场了

禁止写时读,避免了“脏读”,对应隔离级别read committed。

禁止读时写,避免了“不可重复读”,对应隔离级别repeatable read。

而为了避免“幻读”,干脆把整个表给锁住了,只能是serialize了。

隔离级别越高,并行度越低,付出的代价越大。

MySQL默认事务隔离级别为:可重复读(repeatable-read),因此当我们使用MySQL进行实际开发时一般不会发生“脏读”和“不可重复读”。现在可能遇到的问题就是“幻读”,不过MySQL通过多版本并发控制(MVCC)机制解决了该问题。


http://chatgpt.dhexx.cn/article/kcjUw1OZ.shtml

相关文章

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

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

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

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

脏读、幻读和不可重复读

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

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

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

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

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

引用及指针和引用的区别

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

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

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

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

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

C++指针与引用的区别

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

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

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

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

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

指针和引用的区别以及引用与指针基础

1引用: 引用(reference)为对象起了另外一个新的名字通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名: int ival 1024;int& refval ival;int& refval2;//报错:引用必须被初始化…

C++中指针和引用的区别(超详细)

指针和引用主要有以下区别: 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。 引用初始化后不能被改变,指针可以改变所指的对象。 不存在指向空值的引用,但是存在指向空值的…

c++:指针和引用的区别

目录 前言: 1、引用概念上是定义一个变量的别名,而指针是存储一个变量的地址。 2、引用在定义时必须要初始化,但是指针没有要求。 3、引用在初始化时引用一个实体后,就不能再引用其他实体,因为其本质是一个指针常量…

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

1.指针和引用的定义和性质区别: (1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来 的变量实质上是同一个东西,只不过是原变量的一个别…

C++中的指针与引用

写在前面 指针和引用形式上很好区别,但是他们似乎有相同的功能,都能够直接引用对象,对其进行直接的操作。但是什么时候使用指针?什么时候使用引用呢?这两者很容易混淆,在此我详细介绍一下指针和引用,力争将最真实的一…

引用和指针概念及区别

一、引用和指针 指针:指针是一个特殊的变量,它里面存储的的数值为内存里的一个地址,通过*访问内存地址所指向的值 引用:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内…

01背包.动态规划.c语言实现

二维dp数组01背包 确定dp数组以及下标的含义 使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少 2.递推公式:dp[i][j]dp[i-1][j-weight]value[i] dp[i][j]dp[i-1][j] 3.数组初始…

【动态规划】背包问题

注意:for j in range(1,m1):是枚举所有的情况,不用一一判断放入物品后背包容量减少后j的变化。为什么从1开始,因为0已经写出来了,即dp[i-1][j]dp[i-1][j-0*a[i-1]]0*v[i-1]。 01背包无价值 dp定义:前i项物…

动态规划——背包问题(01背包问题)

动态规划——背包问题(01背包问题)01背包问题(求最大价值):问题优化01背包问题(求方案数): 动态规划——背包问题(01背包问题) 01背包问题(求最大价值): 有N件物品和一个最多能背重量为W的背包…