MySQL的可重复读级别能解决幻读吗?MYSQL指的幻读到底是什么?

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 首先,要搞懂这个问题,我们需要先了解Mysql事务的四种隔离级别以及平常我们说的幻读的概念。由于这俩概念比较简单,强哥在这几就简单的带大家复习一下。

01提出问题


首先,要搞懂这个问题,我们需要先了解Mysql事务的四种隔离级别以及平常我们说的幻读的概念。由于这俩概念比较简单,强哥在这几就简单的带大家复习一下。


Mysql事务的隔离级别


SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。


Read Uncommitted(读取未提交内容)


在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)


这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。


Repeatable Read(可重读)


这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。


简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。


Serializable(可串行化)


这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。


12.png


Mysql的幻读


Mysql的幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。


举个例子:事务T1:


11.png


事务T2:


10.png


step1 T1: SELECT * FROM `users` WHERE `id` = 1;

step2 T2: INSERT INTO `users` VALUES (1, 'big cat');

step3 T1: INSERT INTO `users` VALUES (1, 'big cat');

step4 T1: SELECT * FROM `users` WHERE `id` = 1;


T1 :主事务,检测表中是否有 id 为 1 的记录,没有则插入,这是我们期望的正常业务逻辑。


T2 :干扰事务,目的在于扰乱 T1 的正常的事务执行。


在 RR 隔离级别下,step1、step2 是会正常执行的,step3 则会报错主键冲突,对于 T1 的业务来说是执行失败的,这里 T1 就是发生了幻读,因为 T1 在 step1 中读取的数据状态并不能支撑后续的业务操作,T1:“见鬼了,我刚才读到的结果应该可以支持我这样操作才对啊,为什么现在不可以”。T1 不敢相信的又执行了 step4,发现和 setp1 读取的结果是一样的(RR下的 MMVC机制)。此时,幻读无疑已经发生,T1 无论读取多少次,都查不到 id = 1 的记录,但它的确无法插入这条他通过读取来认定不存在的记录(此数据已被T2插入),对于 T1 来说,它幻读了。


其实产生幻读的原因就是:行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。


02解决问题


那么我们在可重复度的情况下是不是就无法解决幻读了呢?


当然不是,其实 RR 也是可以避免幻读的,通过对 select 操作手动加 行X锁(SELECT ... FOR UPDATE 这也正是 SERIALIZABLE 隔离级别下会隐式为你做的事情),同时还需要知道,即便当前记录不存在,比如 id = 1 是不存在的,当前事务也会获得一把记录锁(因为InnoDB的行锁锁定的是索引,故记录实体存在与否没关系,存在就加 行X锁,不存在就加 next-key lock间隙X锁),其他事务则无法插入此索引的记录,故杜绝了幻读。


9.png


id = 1 的记录不存在,开始执行事务:


step1: T1 查询 id = 1 的记录并对其加 X锁

step2: T2 插入 id = 1 的记录,被阻塞

step3: T1 插入 id = 1 的记录,成功执行(T2 依然被阻塞中),T1 提交(T2 唤醒但主键冲突执行错误)


T1事务符合业务需求成功执行,T2干扰T1失败。


这里我们就展示下 id = 1 的记录不存在的场景,FOR UPDATE 也会对此 “记录” 加锁,要明白,InnoDB 的行锁(gap锁是范围行锁,一样的)锁定的是记录所对应的索引,且聚簇索引同记录是直接关系在一起的。


由此可知,通过添加间隙锁可以解决幻读的问题。


03幻读误区


mysql 的幻读和传统意义上的幻读又有所区别,这点尤其要注意。


我们平常说的幻读是:事务1查询id<10的记录时,返回了2条记录,接着事务2插入了一条id为3的记录,并提交。接着事务1查询id<10的记录时,返回了3条记录,说好的可重复读呢?结果却多了一条数据。


但是,由于Mysql存在MVCC,解决了这种情况下的幻读,举例如下:


表中的数据如下,设置隔离级别为可重复读。


8.png


执行如下操作:


事务
session 1 session 2
T1

start transaction;

selectcount(*) from tmp.account where id <= 10;

 

输出:2


T2

start transaction;

insertinto tmp.account (id, name, balance) value(3, 'wang', 0);

selectcount(*) from tmp.account where id <= 10;

commit;


输出:3

T1

selectcount(*) from tmp.account where id <= 10;

commit


输出:2



由T3我们可以看出,其实Mysql的MVCC机制,使我们不会有上述幻读的问题。


而Mysql的幻读,其实具体指的是事务在插入事先检测不存在的记录时,惊奇的发现这些数据已经存在了,之前的检测读获取到的数据如同鬼影一般。也就是第一部分举例的情况。


所以,今后在遇到mysql相关幻读的问题时,一定要多加注意有所区别

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
SQL 关系型数据库 MySQL
【Mysql】数据库事务,脏读、幻读、不可重复读
【Mysql】数据库事务,脏读、幻读、不可重复读
【Mysql】数据库事务,脏读、幻读、不可重复读
|
关系型数据库 MySQL 数据库
MySQL事务的隔离级别以及脏读、幻读和不可重复读
MySQL事务的隔离级别以及脏读、幻读和不可重复读
|
SQL 关系型数据库 MySQL
你知道MySQL是如何解决幻读的吗?
你知道MySQL是如何解决幻读的吗?
385 0
你知道MySQL是如何解决幻读的吗?
|
SQL Oracle 关系型数据库
Mysql模拟事物的隔离级别【脏读、不可重复读、幻读】
Mysql模拟事物的隔离级别【脏读、不可重复读、幻读】
201 0
Mysql模拟事物的隔离级别【脏读、不可重复读、幻读】
|
SQL 关系型数据库 MySQL
【MySQL】可重复读隔离级别是否解决了幻读?
面试官:可重复读隔离级别是否解决了幻读了吗?不清楚是吧,没关系,那我们今天面试就先到这。
238 0
【MySQL】可重复读隔离级别是否解决了幻读?
|
关系型数据库 MySQL 数据库
Mysql-详解脏读、不可重复读、幻读
Mysql-详解脏读、不可重复读、幻读
Mysql-详解脏读、不可重复读、幻读
|
关系型数据库 MySQL 数据库
MySQL事务的隔离级别以及脏读、幻读和不可重复读
ACID,是指数据库管理系统(DBMS)在写入或更新的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)
152 0
MySQL事务的隔离级别以及脏读、幻读和不可重复读
|
缓存 关系型数据库 MySQL
MySQL中脏读、不可重复读、幻读
简单聊聊mysql的脏读、不可重复读、幻读
181 0
|
SQL 关系型数据库 MySQL
MySQL到底在RR层面解决幻读了吗?
hi,好久不见。 最近工作上有一些变动,文章很少更新。不过平时还是有收集一些文章idea,后面有空会慢慢写~
1077 0
|
9天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
24 1