你知道MySQL是如何解决幻读的吗?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: 你知道MySQL是如何解决幻读的吗?

前言


SQL标准中定义了4种隔离级别,分别是读未提交、读已提交、可重复读以及序列化。不同的隔离级别下,可以解决不同的并发问题,如下图所示。当然MySQL也基本遵循了这个标准,但是在实现上稍有不同。

本文重点探讨下MySQL是如何解决幻读问题的,首先串行化隔离级别铁定是可以解决所有的并发问题,相当于每个事务按顺序执行,但是性能很差,不是本文重点讨论对象。实际上MySQL默认的事务隔离级别是可重复读,难道这种隔离级别下MySQL就不管幻读问题了吗?其实不是的,本文就带大家一起看看MySQL在可重复读隔离级别下是如何解决幻读问题的。

1671201582399.jpg


什么是幻读?


幻读是指一个事务中按照某个条件先后两次读取数据库,两次读取结果的条数不同,更加强调的是读到了之前没有读到的数据,这种现象称为幻读

举个例子:

1671201596080.jpg

  1. 事务A一开始只读取到‘张三’的数据记录。
  2. 然后另外一个事务B插入了‘赵六’的数据记录。
  3. 事务A再次读取,发现了‘张三’、‘赵六’两条数据,明明同一个事务,同样的查询条件,前后两次读取,多了一条记录,相当于“幻影”,这种情况就是幻读。


什么是普通读和当前读?


其实读这个操作也有两种情况,一种是普通读,就像上面例子的那样,还有一种就是当前读。不同的读模式,MySQL在可重复读隔离级别下的实现方式也是不一样的。

普通读

普通读又叫快照读,也就是利用MVCC机制读取快照中的数据。不加锁的简单的SELECT 都属于快照读,比如这样:

SELECT * FROM user WHERE ...
  • 快照读是基于MVCC实现的,提高了并发的性能,降低开销
  • 大部分业务代码中的读取都属于快照读


当前读


当前读读取的是记录的最新版本,读取时会对读取的记录进行加锁, 其他事务就有可能阻塞。加锁的 SELECT,或者对数据进行增删改都会进行当前读。比如:

SELECT * FROM user LOCK IN SHARE MODE; # 共享锁
SELECT * FROM user FOR UPDATE; # 排他锁
INSERT INTO user values ... # 排他锁
DELETE FROM user WHERE ... # 排他锁
UPDATE user SET ... # 排他锁
  • update、delete、insert语句虽然没有select, 但是它们也会先进行读取,而且只能读取最新版本。

那不同的读模式下,MySQL分别是如何避免幻读的呢?请接着往下看。


普通读是如何避免幻读的?


MySQL在可重复读隔离级别下,是通过MVCC机制避免幻读的。

MVCC机制,可以简单理解成在事务启动的时候对数据库拍了个“快照”,它保留了那个时刻数据库的数据状态,那么这个事务后续的读取都可以从这个“快照”中获取,哪怕其他事务新加了数据,也不会影响到“快照”中的数据,也就不会出现幻读了。

关于MVCC的详细机制强烈你阅读本文看完这篇还不懂MySQL的MVCC机制算我输

1671201637759.jpg

  • 事务A在启动的时候创建了一个“快照”,查询出结果“小红,小蓝”
  • 后续事务B插入一条记录“小飞”,提交
  • 然后事务A再次同样查询条件查询,它会使用“快照”读取,所以还是“小红,小蓝”

小结: 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。


当前读是如何避免幻读的?


普通读(快照读)实际上读取的是历史版本中的数据,但一直用这种方式读取在某些场景下是有问题的。

假设你要 update 一个记录,但是另一个事务已经 delete 这条记录并且提交事务了,这样不是会产生冲突吗,所以 update 的时候肯定要知道最新的数据。也就是要做当前读

那么针对当前读,MySQL在可重复读隔离级别下是如何避免幻读的呢?

也就是说不能读取“快照”了,因为你要最新状态的数据,那么能不能在当前读的时候,对这段区间都加上锁,让别的事务阻塞,无法插入。因此,MySQLInnoDB引擎为了解决可重复读隔离级别使用当前读而造成的幻读问题,引入了间隙锁

1671201682535.jpg

表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

举个例子:

1671201693353.jpg

  • 事务A的for_update是属于当前读,它会对锁定 id 范围 (2, +∞] ,相当于理解是间隙锁。
  • 事务B插入了id=5的数据,(2, +∞]范围被锁定了,所以无法插入,阻塞。
  • 通过这种加锁阻塞的方式,也可以避免幻读。

小结: 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。

关于MySQL锁的更多内容查看盘点MySQL中的各种锁


总结


MySQL默认采用的隔离级别是可重复读,在这种隔离级别下不同的读模式,针对幻读问题采用了不同解决方案:

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。
  • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。

但是,强调一点的是,MySQL在可重复读级别下,并没有完完全全的解决幻读问题,特别是在一个事务的快照读和当前读穿插使用的场景下,还是会出现幻读的情况,比如下图所示。

1671201723276.jpg

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
运维 DataWorks 关系型数据库
DataWorks产品使用合集之DataWorks还有就是对于mysql中的表已经存在数据了,第一次全量后面增量同步的步骤如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
47 2
|
1月前
|
关系型数据库 MySQL Apache
实时计算 Flink版操作报错之mysql整库同步到doris连接器报错,如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
1月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版操作报错合集之程序初始化mysql没有完成就报错如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
211 58
|
16天前
|
分布式计算 DataWorks 关系型数据库
DataWorks操作报错合集之在数据集成到MySQL时,遇到特殊字符导致的脏数据如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
1月前
|
关系型数据库 MySQL 数据处理
实时计算 Flink版产品使用合集之在同步MySQL的时候卡在某个binlog文件处如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
实时计算 Flink版产品使用合集之在同步MySQL的时候卡在某个binlog文件处如何解决
|
1月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用合集之同步MySQL时,发现Timestamp字段少八个小时,该如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
Oracle 关系型数据库 MySQL
实时计算 Flink版操作报错合集之用CTAS从mysql同步数据到hologres,改了字段长度,报错提示需要全部重新同步如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
66 8
|
1月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用合集之使用 MySQL CDC 进行数据同步时,设置 server_id 参数如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版操作报错之遇到MySQL服务器的时区偏移量(比UTC晚18000秒)与配置的亚洲/上海时区不匹配,如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
1月前
|
关系型数据库 MySQL 数据库
实时计算 Flink版操作报错合集之sqlserver mysql都用的胖包,sqlserver的成功了,mysql报这个错如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
56 6