先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的。

方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作;2.给涉及到的行两端加间隙锁(Gap Lock)防止新增行写入;从而解决了幻读问题。

下面,让我带大家从原理出发,一起来搞懂MySQL并发问题 -- “幻读”。如果有好的看法,咱们评论见吧。

小伙伴想精准查找自己想看的MySQL文章?喏 → MySQL专栏目录 | 点击这里

目录

什么是幻读

一、幻读定义

二、幻读示例

三、幻读出现的场景

四、解决幻读问题的必要性

如何解决幻读

一、原理解读

二、next-key lock

一张照片的故事


什么是幻读

要知道什么是幻读,首先要知道以下四点:

一、幻读定义

幻读是指在同一个事务中,存在前后两次查询同一个范围的数据,但是第二次查询却看到了第一次查询没看到的行,一般情况下特指事务执行中新增的其他行。

二、幻读示例

测试表数据:

mysql> select * from LOL;
+----+--------------+--------------+-------+
| id | hero_title   | hero_name    | price |
+----+--------------+--------------+-------+
|  1 | 刀锋之影     | 泰隆         |  6300 |
|  2 | 迅捷斥候     | 提莫         |  6300 |
|  3 | 光辉女郎     | 拉克丝       |  1350 |
|  4 | 发条魔灵     | 奥莉安娜     |  6300 |
|  5 | 至高之拳     | 李青         |  6300 |
|  6 | 无极剑圣     | 易           |   450 |
|  7 | 疾风剑豪     | 亚索         |  6300 |
+----+--------------+--------------+-------+
7 rows in set (0.00 sec)

下面是一个出现幻读情况示例,我们一起来看一下;

时刻T Session A Session B Session C
T1

begin;

-- Query1

select * from LOL where price=450 for update;

Result:(6,'无极剑圣',450)

   
T2   update LOL set price=450 where hero_title = '疾风剑豪'  
T3

-- Query2

select * from LOL where price=450 for update;

Result:(6,'无极剑圣',450),(7,'疾风剑豪',450)

   
T4     insert into LOL values(10,'雪人骑士','努努','450')
T5

-- Query3

select * from LOL where price=450 for update;

Result:(6,'无极剑圣',450),(7,'疾风剑豪',450),(10,'雪人骑士',450)

   
T6 commit;    

可以看到,session A 里执行了三次查询,分别是 Q1、Q2 和 Q3。它们的 SQL 语句相同,都是 select * from LOL where price=450 for update。这个语句的意思你应该很清楚了,查所有 price=450 的行,而且使用的是当前读,并且加上写锁。现在,我们来看一下这三条 SQL 语句,分别会返回什么结果。

  1. Q1 只返回 "无极剑圣" 这一行;
  2. 在 T2 时刻,session B 把 "疾风剑豪" 这一行的 price 值改成了 450,因此 T3 时刻 Q2 查出来的是 "无极剑圣""疾风剑豪" 这两行;
  3. 在 T4 时刻,session C 又插入一行 (10,'雪人骑士','努努','450'),因此 T5 时刻 Q3 查出来 price = 450 的是"无极剑圣" 、"疾风剑豪" 和 "雪人骑士" 这三行。

其中,Q3 读到 (10,'雪人骑士',450) 这一行的现象,被称为“幻读”。也就是说,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

三、幻读出现的场景

  1. 幻读出现在可重复读(RR)隔离级别下,普通的SELECT查询就是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。(当前读会生成行锁,但行锁只能锁定存在的行,针对新插入的操作没有限定)
  2. 上面 session B 的修改结果,被 session A 之后的 select 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。

因为这三个查询都是加了 for update,都是当前读。而当前读的规则,就是要能读到所有已经提交的记录的最新值。并且,session B 和 sessionC 的两条语句,执行后就会提交,所以 Q2 和 Q3 就是应该看到这两个事务的操作效果,而且也看到了,这跟事务的可见性规则并不矛盾。

四、解决幻读问题的必要性

在高并发数据库系统中,需要保证事务与事务之间的隔离性,还有事务本身的一致性。

如何解决幻读

如果你看到了这篇文章,那么我会默认你了解了脏读 、不可重复读与可重复读。如果还不清楚可以先参阅《上个厕所的功夫,搞懂MySQL事务隔离级别》

场景如上,场景隔离级别为RR,当前读。

一、原理解读

那么幻读能仅通过行锁解决么?答案是否定的,如上面示例,首先说明一下,select xx for update(当前读)是将所有条件涉及到的(符合where条件)行加上行锁。但是,就算我在select xx for update 事务开启时将所有的行都加上行锁。那么也锁不住Session C新增的行,因为在我给数据加锁的时刻,压根就还没有新增的那行,自然也不会给新增行加上锁。

所以要解决幻读,就必须得解决新增行的问题。

现在你应该明白了,产生幻读的原因是:行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。顾名思义,间隙锁,锁的就是两个值之间的空隙。比如文章开头的表 LOL,初始化插入了 7 个记录,这就产生了 8 个间隙。

二、next-key lock

这样,当你执行 select * from LOL where hero_title = '疾风剑豪' for update 的时候,就不止是给数据库中已有的 7 个记录加上了行锁,还同时加了 8 个间隙锁。这样就确保了无法再插入新的记录,也就是Session C在T4新增(10,'雪人骑士','努努','450') 行时,由于ID大于7,被间隙锁(7,+∞)锁住。

在一行行扫描的过程中,不仅将给行加上了行锁,还给行两边的空隙,也加上了间隙锁。MySQL将行锁 + 间隙锁组合统称为 next-key lock,通过 next-key lock 解决了幻读问题。

注意

next-key lock的确是解决了幻读问题,但是next-key lock在并发情况下也经常会造成死锁。死锁检测和处理也会花费时间,一定程度上影响到并发量。

参考资料

《高性能MySQL》

《丁奇MySQL实战45讲》

一张照片的故事

或许京剧自己都没想到
清末的洋人,民国的战火都没能毁了它
最后居然是衰落在中国人自己的手里

灵魂拷问,MySQL到底能否解决幻读问题相关推荐

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

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

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

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

  3. mysql 解决了幻影读_MySQL到底能否解决幻读问题

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

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

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

  5. MySQL是如何解决幻读

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

  6. MySql是怎么解决幻读的。

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

  7. MySQL InnoDB如何解决幻读?

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

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

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

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

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

最新文章

  1. Java项目:网上电商系统(java+SSM+mysql+maven+tomcat)
  2. CPA相关功耗分析(二)
  3. emacs 新手笔记(四) —— 使用 dired 完成一些简单的文件和目录操作
  4. where is application controller bound to application main view
  5. Shell应用:批量将文件编码由gbk转utf-8
  6. 可扩展的编程语言——Scala
  7. Android之AsyncTask学习笔记
  8. 开源的人品测试机 (windows版)
  9. android之AlarmManager 全局定时器
  10. 解决:‘webpack-dev-server‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件
  11. gamit批量下载精密星历shell脚本
  12. jenkins教程菜鸟_jenkins 入门教程(上)
  13. python打造微信聊天机器人_求问各位大佬,如何用Python写一款微信聊天机器人?...
  14. java rxtx 串口_Java使用RXTX进行串口SerialPort通讯
  15. C语言 | 逻辑运算符
  16. 10个值得推荐的免费设计模板网站
  17. Apache Kylin PMC 马洪宾:开源,就是一场“螺旋上升”的旅程
  18. java是多线程_Java之多线程(一)
  19. 求生之路2服务器模式修改参数,求生之路2参数修改控制台命令_求生之路2参数修改控制台命令方法_牛游戏网...
  20. 吉里吉里2 2.30版正式发布了

热门文章

  1. Hexo博客添加搜索功能
  2. uniapp 画布绘制二维码,图片,文字的方法
  3. 知识点6--Docker的镜像命令
  4. k8s面试中最常见的50个问题(翻译)
  5. 计算球的体积-java
  6. 埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛L
  7. Ubuntu 17.10 中文无忧版
  8. Homework 1: COVID-19 Cases Prediction (Regression)
  9. Synology 群晖RAID1数据恢复
  10. 022减淡工具,加深工具与海绵工具