MySql是怎么解决幻读的。
创始人
2025-05-29 08:29:44

首先幻读是什么?

根据MySQL文档上面的定义

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
幻读指的是在一个事务内,同一SELECT语句在不同时间执行,得到不同的结果集时,就会发生所谓的幻读问题。

可以看看下面的例子:

这是网上找的一张图(事务的务字写错了,不过不影响我们理解)
在这里插入图片描述
假设这个例子中的MySQL的隔离级别是提交读,也就是一个事务内可以读到其他事务提交后的结果。

那么事务1第一次查询dept表中所有部门时,结果是没有"研发部",但是由于隔离级别是提交读,在事务2插入“研发部”这一行数据后,并且提交后,事务1是可以读取到的,所以第二次查询时,结果集中会有“研发部”。这就是幻读。
SELECT语句分类
首先我们的SELECT查询分为快照读和实时读,快照读通过MVCC(并发多版本控制)来解决幻读问题,实时读通过行锁来解决幻读问题。

快照读

1.1 快照读是什么?

因为MySQL默认的隔离级别是可重复读,这种隔离级别下,我们普通的SELECT语句都是快照读,也就是在一个事务内,多次执行SELECT语句,查询到的数据都是事务开始时那个状态的数据(这样就不会受其他事务修改数据的影响),这样就解决了幻读的问题。

1.2 那么innodb是怎么解决快照读的幻读问题的?

快照读就是每一行数据中额外保存两个隐藏的列,插入这个数据行时的版本号,删除这个数据行时的版本号(可能为空),滚动指针(指向undo log中用于事务回滚的日志记录)。

事务在对数据修改后,进行保存时,如果数据行的当前版本号与事务开始取得数据的版本号一致就保存成功,否则保存失败。

当我们不显式使用BEGIN来开启事务时,我们执行的每一条语句就是一个事务,每次开始事务时,会对系统版本号+1作为当前事务的ID。

1.2.1 插入操作

插入一行数据时,将事务的ID作为数据行的创建版本号。

1.2.2 删除操作

执行删除操作时,会将原数据行的删除版本号设置为当前事务的ID,然后根据原数据行生成一条INSERT语句,写入undo log,用于事务执行失败时回滚。delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。但是会将数据行的删除版本号设置为当前的事务的ID,这样后面的事务B即便查到这行数据由于事务B的ID>删除版本号,也会忽略这条数据。

1.2.3 更新操作

更新时可以简单的认为是先将旧数据删除,然后插入一条新数据。

所以执行更新操作时,其实是会将原数据行的删除版本号设置为当前事务的ID,生成一条INSERT语句,写入undo log,用于事务执行失败时回滚。插入一条新的数据,将事务的ID作为数据行的的创建版本号。

1.2.4 查询操作

数据行要被查询出来必须满足两个条件,

数据行删除版本号为空或者>当前事务版本号的数据(否则数据已经被标记删除了)
创建版本号<=当前事务版本号的数据(否则数据是后面的事务创建出来的)
简单来说,就是查询时,

如果该行数据没有被加行锁中的X锁(也就是没有其他事务对这行数据进行修改),那么直接读取数据(前提是数据的版本号<=当前事务版本号的数据,不然不会放到查询结果集里面)。
该行数据被加了行锁X锁(也就是现在有其他事务对这行数据进行修改),那么读数据的事务不会进行等待,而是回去undo log端里面读之前版本的数据(这里存储的数据本身是用于回滚的),在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据。
1.3 补充资料:undo log段是什么?

undo_log是一种逻辑日志,是旧数据的备份。有两个作用,用于事务回滚和为MVCC提供老版本的数据。

可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

1.3.1 用于事务回滚

当事务执行失败,回退时,会读取这行数据的滚动指针(指向undo log中用于事务回滚的日志记录),就可以在undo log中找到相应的逻辑记录,读取到相应的回滚语句,执行进行回滚。

1.3.2 为MVCC提供老版本的数据

当读取的某一行被其他事务锁定时(也就是有其他事务正在改这行数据),它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户进行快照读。在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据(也就是比正在修改这行数据的事务ID修改前的数据。)。

实时读

2.1 实时读是什么?

如果说快照读总是读取事务开始时那个状态的数据,实时读就是查询时总是执行这个查询时数据库中的数据。

一般使用以下这两种查询语句进行查询时就是实时读。

SELECT *** FOR UPDATE 在查询时会先申请X锁SELECT *** IN SHARE MODE 在查询时会先申请S锁
首先看一个实时读产生幻读的案例:
在这里插入图片描述
这是《MySQL技术内幕++InnoDB存储引擎++第2版》里面的一张图,就是先将隔离级别设置为提交读,这样第一次执行 SELECT…FOR UPDATE查询出来的数据是a:4,事务B插入了一条新的数据,再次执行 SELECT…FOR UPDATE语句时,查询出来就是a:4,a:5两条数据,这就是幻读的问题。
2.1 那么innodb是怎么解决实时读的幻读问题的?

如果我们不在一开始将将隔离级别设置为提交读,其实是不会产生幻读问题的,因为MySQL的默认隔离级别是可重复读,在这种情况下,我们执行第一次 SELECT…FOR UPDATE查询语句是,其实是会先申请行锁,因为一开始数据库就只有a:4一行数据,那么加锁区间其实是(负无穷,4](4,正无穷)
我们查询条件是a>2,上面两个加锁区间都会可能有数据满足条件,所以会申请行锁中的next-key lock,是会对上面这两个区间都加锁,这样其他事务不能往这两个区间插入数据,事务B会执行插入时会一直等待获取锁,直到事务A提交,释放行锁,事务B才有可能申请到锁,然后进行插入。这样就解决了幻读问题。

相关内容

热门资讯

【C语言】结构体(详解) 目录1. 结构体基本知识1.1 结构体声明1.2 结构体的自引用1.3 结构体变量的定义和初始化2....
百万保时捷停斗香上被烧毁,斗香... 今天给各位分享百万保时捷停斗香上被烧毁,斗香烧车还真不稀奇的知识,其中也会对进行解释,如果能碰巧解决...
小提琴价格一般多少钱一把?有什... 本篇文章极速百科给大家谈谈小提琴价格一般多少钱一把?有什么不同吗?,以及小提琴大约多少钱?对应的知识...
驾龄多少年可以上高速?高速几年... 本篇文章极速百科给大家谈谈驾龄多少年可以上高速?高速几年驾龄可以上,以及几年驾龄才能上高速公路对应的...
景逸X3景逸X3最新报价-图片... 本篇文章极速百科给大家谈谈景逸X3景逸X3最新报价-图片-参数,以及景逸x3汽车之家对应的知识点,希...
Pr 复古胶片老电影回忆效果 哈喽,各位小伙伴!今天我们来学习一下如何制作复古胶片老电影回忆效果&#x...
【STM32学习】WWDG窗口... 【STM32学习】WWDG窗口看门狗🐕1、图展示WWDG原理2、复位、中断条件3、溢...
瑞祥是什么意思(瑞祥是什么意思... 今天给各位分享瑞祥是什么意思的知识,其中也会对瑞祥是什么意思?进行解释,如果能碰巧解决你现在面临的问...
黄鼠狼的天敌(黄鼠狼的天敌是什... 今天给各位分享黄鼠狼的天敌的知识,其中也会对黄鼠狼的天敌是什么动物百度百科进行解释,如果能碰巧解决你...
famous怎么读(famou... 今天给各位分享famous怎么读的知识,其中也会对famous怎么读英语进行解释,如果能碰巧解决你现...
中国奉行什么的国防政策(中国奉... 本篇文章极速百科给大家谈谈中国奉行什么的国防政策,以及中国奉行的是什么样的国防政策?对应的知识点,希...
【Hello Linux】进程... 作者:@小萌新 专栏:@Linux 作者简介࿱...
STM32F103指南者开发板... 1 前言 使用STM32F103指南者开发板,安装了Keil5,使用St...
二维数组的表现及应用 1 问题在Java数组中,数组是一种常遇见的表现形式。对于一维数组在最近的学习已经非常...
《程序员面试金典(第6版)》面... 题目描述 给定两个整型数字 N 与 M,以及表示比特位置的 i 与 j(...
什么叫丹霞地貌(中国七大丹霞景... 本篇文章极速百科给大家谈谈什么叫丹霞地貌,以及中国七大丹霞景区对应的知识点,希望对各位有所帮助,不要...
白色骐达mdashmdash好... 今天给各位分享白色骐达mdashmdash好看实用的两箱车的知识,其中也会对白色骐达改装图片进行解释...
裤子尺码28是多大(裤子尺码2... 本篇文章极速百科给大家谈谈裤子尺码28是多大,以及裤子尺码28是多大码对应的知识点,希望对各位有所帮...
进口奔驰s300价格多少(进口... 今天给各位分享进口奔驰s300价格多少的知识,其中也会对进口奔驰s300价格多少钱一辆进行解释,如果...
Keras 的模型(Model... 我们来做个 TensorFlow 的快速入门模型分享。 这次的学习目标就是模型构建的一些相关 API...
串行通信协议(I2C、SPI、... I2C(Inter-Integrated Circuit) 1.简单的双向两线制总线协议标准、半双...
石化团购:放价不打烊,14个汽... 今天给各位分享石化团购:放价不打烊,14个汽车品牌请您挑选!的知识,其中也会对石化团购的商品是真的吗...
tf金箔润唇膏是不是死亡芭比粉... 今天给各位分享tf金箔润唇膏是不是死亡芭比粉的知识,其中也会对tom ford金箔唇膏进行解释,如果...
日本也开始山寨了?造最强悍马,... 本篇文章极速百科给大家谈谈日本也开始山寨了?造最强悍马,比美国的还大一号!,以及日本山寨历史对应的知...
走应急车道一天内最多罚几次?应... 今天给各位分享走应急车道一天内最多罚几次?应急车道抓拍原理的知识,其中也会对应急车道行驶多久会被拍进...
代码分支管理:主干发布分支开发... 大家好,我是rainbowzhou。 上篇文章代码分支管理中,我介绍了3...
蓝桥杯C++组怒刷50道真题(... 🌼深夜伤感网抑云 - 南辰Music/御小兮 - 单曲 - 网易云音乐 ...
2015款林肯MKX空间怎么样... 本篇文章极速百科给大家谈谈2015款林肯MKX空间怎么样林肯MKX购车手册,以及2015款林肯mkx...
夏利n7怎么样能买么?的简单介... 今天给各位分享夏利n7怎么样能买么?的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,别忘...
特斯拉宣布已在中国建立数据中心... 本篇文章极速百科给大家谈谈特斯拉宣布已在中国建立数据中心,以实现数据存储本地...,以及特斯拉中国数...