首先幻读是什么?

根据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才有可能申请到锁,然后进行插入。这样就解决了幻读问题。

MySql是怎么解决幻读的。相关推荐

  1. mysql 什么是幻读_何为幻读?MySQL又是如何解决幻读的?

    一.什么是幻读 在一次事务里面,多次查询之后,查询的结果集的个数不一致的情况叫做幻读.而多出来或者少的哪一行被叫做 幻行 二.为什么要解决幻读 在高并发数据库系统中,需要保证事务与事务之间的隔离性,还 ...

  2. MySQL是怎么解决幻读问题的?

    问题分析 首先幻读是什么? 根据MySQL文档上面的定义 The so-called phantom problem occurs within a transaction when the same ...

  3. 灵魂拷问,MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  4. MySQL是如何解决幻读

    前言 事务的隔离级别有四种,读未提交,读已提交,可重复读和串行化,下面结合具体的问题,在mysql中innodb引擎是怎么解决幻读的? 一.相关问题 1.什么是幻读 幻读:一次事务里,多次查询后,结果 ...

  5. mysql什么场景下要防止幻读_灵魂拷问,MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  6. MySQL InnoDB如何解决幻读?

    1 数据准备 CREATE TABLE `t` (`id` int(11) NOT NULL,`c` int(11) DEFAULT NULL,`d` int(11) DEFAULT NULL,PRI ...

  7. 面试官:MySQL是如何解决幻读的?

    介绍 众所周知,在不同隔离级别下,会发生如下问题. √ 为会发生,×为不会发生 隔离级别 脏读 不可重复读 幻读 read uncommitted(未提交读) √ √ √ read committed ...

  8. MySQL 到底是怎么解决幻读的?

    作者:LastSun https://www.cnblogs.com/wdy1184/p/10655180.html 一.什么是幻读 在一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读.而 ...

  9. mysql脏读,幻读,不可重复读以及间隙所解决幻读

    1.数据脏读 事务a修改了某条数据,然后事务b读取了事务a修改的该条数据,然后事务a由于某些原因,事务a回滚了,这样事务b读到的数据就和回滚的数据不同了,这时事务b读取的数据就是脏数据.概况一句话就是 ...

最新文章

  1. China SAFe DAY 2020中国规模化敏捷大会圆满落幕
  2. 一句话总结卷积神经网络
  3. matlab字符处理
  4. Python基础day02【if结构、if elif 结构、while循环、for循环、Break和continue、Debug 调试、三目运算、循环 else 结构】
  5. boost::fusion::cons用法的测试程序
  6. SharePoint GridView的使用2——DataSourceView的使用
  7. jsp页面验证码(完整实例)
  8. linux中的ip地址、子网掩码、端口
  9. 自步对比学习: 充分挖掘无监督学习样本
  10. 【快速幂】a^b%p问题
  11. 【优化预测】基于matlab人工鱼群算法优化BP神经网络预测【含Matlab源码 523期】
  12. python 规则引擎 drools_Drools规则引擎入门指南(一)
  13. ppt模板怎样用到html中,教你如何自己制作PPT模板及使用模板方法图文介绍
  14. Metasploit入门使用手册
  15. React 之 Expected an assignment or function call and instead saw an expression 解决办法
  16. 航海家软件公式全破解
  17. 苹果蓝牙耳机太贵了买哪个替代?苹果蓝牙耳机平替推荐
  18. 《完全用Linux工作》
  19. jzoj4024 [佛山市选2015]石子游戏
  20. 爬取大众点评评论-字体加密解析!这个网站很难搞出来!

热门文章

  1. 树莓派上的tensorflow视频识别+远程视频流传输
  2. 【STM32】谈谈STM32F10XX的定时器通道复用功能重映射
  3. 验证码绕过与密码找回漏洞
  4. html 悬停 二级菜单,使用HTML+CSS实现鼠标划过的二级菜单栏!
  5. 一文搞懂高速电路中的电源设计
  6. 深夜十点半(二)——我的第二个Python程序“购物车”
  7. lucene6.0SmartChineseAnalyzer和IK Analyzer比较
  8. uniapp base64转图片
  9. 1. 基于MSYS2的Mingw-w64 GCC搭建Windows下C++开发环境
  10. linux的回车和换行符,【Base】linux和windows下的“回车符”和“换行符”