MySQL的MVCC是否解决幻读

MySQL的MVCC是否解决幻读

MySQL事务隔离级别

✓ 读未提交(Read Uncommitted):最低的隔离级别,会读取到其他事务还未提交的内容,存在脏读。

✓ 读已提交(Read Committed):读取到的内容都是已经提交的,可以解决脏读,但是存在不可重复读。

✓ 可重复读(Repeatable Read):在一个事务中多次读取时看到相同的内容,可以解决不可重复读,但是存在幻读。但是在 InnoDB 中不存在幻读问题,对于快照读,InnoDB 使用 MVCC 解决幻读,对于当前读,InnoDB 通过 gap locks 或 next-key locks 解决幻读。

✓ 串行化(Serializable):最高的隔离级别,串行的执行事务,没有并发事务问题。

MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ)

简单总结一下,MySQL 的 4 种事务隔离级别对应脏读、不可重复读和幻读的关系如下:

事务隔离级别

脏读

不可重复读

幻读

读未提交(READ UNCOMMITTED)

读已提交(READ COMMITTED)

×

可重复读(REPEATABLE READ)

×

×

串行化(SERIALIZABLE)

×

×

×

不可重复度和幻读的区别

不可重复读 主要是说多次读取一条记录, 发现该记录中某些列值被修改过。

幻读 主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)。(可以参考)

模拟幻读问题

环境准备

事务相关的命令

# 查看 MySQL 版本
select version();# 开启事务
start transaction;# 提交事务
commit;# 回滚事务
rollback;

MySQL8查询事务隔离级别的命令

select @@global.transaction_isolation,@@transaction_isolation;

通过以下 SQL 可以设置当前客户端的事务隔离级别:

set session transaction isolation level 事务隔离级别;

事务隔离级别的值有 4 个:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。

测试数据准备

创建测试数据库和表信息,执行 SQL 如下:

-- 创建数据库
drop database if exists testdb;
create database testdb;
use testdb;
-- 创建表
create table userinfo(
  id int primary key auto_increment,
  name varchar(250) not null,
  balance decimal(10,2) not null default 0
);
-- 插入测试数据
insert into userinfo(id,name,balance) values(1,'Java',100),(2,'MySQL',200);

创建的表结构和数据如下:

mysql> select * from userinfo;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | Java  | 100.00  |
|  2 | MySQL | 200.00  |
+----+-------+---------+
2 rows in set (0.00 sec)

幻读模拟

接下来会使用两个窗口(两个客户端)来演示事务在可重复读隔离级别下的幻读的问题。

幻读场景1

在一个事务中明明没有查到主键为 X 的数据,但主键为 X 的数据就是插入不进去,就像某种幻觉一样。 幻读演示的执行流程如下:

执行步骤

客户端1(窗口1)

客户端2(窗口2)

说明

第 1 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=3;

设置事务隔离级别为可重复读;开启事务;查询用户编号为 3 的数据,查询结果为空。

第 2 步

start transaction;

insert into userinfo(id,name,balance) 

values(3,'Spring',100);

commit;

开启事务;添加用户,用户编号为 3;提交事务。

第 3 步

insert into userinfo(id,name,balance)

 values(3,'Spring',100);

窗口 2 添加用户编号为 3 的数据,执行失败。

第 4 步

select * from userinfo where id=3;

查询用户编号为 3 的数据,查询结果为空。

在窗口2中查询id为3的记录为空

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from userinfo where id=3;
Empty set

在窗口1中插入一条id为3的记录

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into userinfo(id,name,balance) values(3,'Spring',100);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

在窗口2中插入id为3的记录

insert into userinfo(id,name,balance) values(3,'Spring',100);

发现插入失败

mysql> insert into userinfo(id,name,balance) values(3,'Spring',100);
1062 - Duplicate entry '3' for key 'userinfo.PRIMARY'

继续在窗口2查询id为3的记录,发现查询不到

mysql> select * from userinfo where id=3;
Empty set

幻读场景2

在一个事务A中一开始查询不到id为X的记录,但在另一个事务B中插入id为X的记录并提交事务后,在事务A中再更新id为X的记录可以更新到,再次查询id为X的记录也可以查询到。

执行步骤

客户端1(窗口1)

客户端2(窗口2)

说明

第 1 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=4;

设置事务隔离级别为可重复读;开启事务;查询用户编号为 4 的数据,查询结果为空。

第 2 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=4;

insert into userinfo(id,name,balance)

 values(4,'Redis',100);

commit;

开启事务;添加用户,用户编号为 4;提交事务。

第 3 步

select * from userinfo where id=4;

查询用户编号为 3 的数据,查询结果为空。此时还未出现幻读问题

第 4 步

update userinfo set name='kafka' 

where id = 4;

更新用户编号为4的记录,更新成功

第 5 步

select * from userinfo where id=4;

查询到用户编号为4的记录,查询到数据,出现幻读问题

第一步,在窗口1中查询id为4的记录,查询结果为空

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=4;Empty set

第二步,在窗口2中查询id为4的记录,查询结果为空。

插入一条id为4 的记录并提交事务

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=4;Empty setmysql> insert into userinfo(id,name,balance) values(4,'Redis',100);Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)mysql> 

第三步,在窗口1中查询id为4 的记录,查询结果为空,此时还没有幻读问题

mysql> select * from userinfo where id=4;Empty set

第四步,在窗口1中更新id为4 的记录,更新成功

mysql> update userinfo set name='kafka' where id = 4;Query OK, 1 row affected (0.00 sec)
Rows matched1  Changed: 1  Warnings0

第五步,在窗口1中查询id为4 的记录,查询到更新后的数据,出现幻读问题

mysql> select * from userinfo where id=4;+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  4 | kafka | 100.00  |
+----+-------+---------+
1 row in set (0.00 sec)

幻读场景3

在事务A中查询id为X的记录查询不到,在事务B中插入一条id为X的记录后,再到事务A中对表执行当前读 for update可以查询到id为X的记录。

执行步骤

客户端1(窗口1)

客户端2(窗口2)

说明

第 1 步

set session transaction isolation
 level repeatable read;

start transaction;

select * from userinfo;

设置事务隔离级别为可重复读;开启事务;查询用户表所有数据,查询结果为4条。

第 2 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=5;

insert into userinfo(id,name,balance) 

values(5,'Pulsar',100);

commit;

开启事务;添加用户,用户编号为 5;提交事务。

第 3 步

select * from userinfo for update;

执行当前读for update,查询出了id为5的记录,出现幻读问题。

第一步,在窗口1中查询用户表的所有记录,查询结果为4条

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo;+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | Java   | 100.00  |
|  2 | MySQL  | 200.00  |
|  3 | Spring | 100.00  |
|  4 | kafka  | 100.00  |
+----+--------+---------+
4 rows in set (0.00 sec)

第二步,在窗口2中查询id为5的记录,查询结果为空。

插入一条id为5 的记录并提交事务

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=5;Empty setmysql> insert into userinfo(id,name,balance) values(5,'Pulsar',100);Query OK, 1 row affected (0.00 sec)
mysql> commit;Query OK, 0 rows affected (0.00 sec)

第三步,窗口1执行当前读for update,查询出了id为5的记录,出现了幻读问题。

mysql> select * from userinfo for update;+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | Java   | 100.00  |
|  2 | MySQL  | 200.00  |
|  3 | Spring | 100.00  |
|  4 | kafka  | 100.00  |
|  5 | Pulsar | 100.00  |
+----+--------+---------+
5 rows in set (0.00 sec)

结论

不可重复读的重点在于 update 和 delete, 而幻读着重强调 insert 操作, 当用户第一次读取时,存在结果集 a, b, c,第二次读取时结果集为 a, b, c, d 即多出来的一条数据 d 表示发送幻读现象。

不可重复读的现象是用户第一次读取,返回结果集 a, 而第二次读取返回结果 a’, 即数据发生了变更. 而 a’ 数据确实由一个已经 committed 的事务修改. 不可重复读现象需要隔离级别为 Repeatable Read 来规避. 在 InnoDB 中使用 MVCC 机制实现。

通常来说, MVCC 的多版本控制并不能保证在 RR 隔离级别下完全避免幻读, 但 InnoDB 通过 MVCC + Next key lock 的方式来保证在 RR 隔离级别下避免部分场景下幻读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/4293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux/ubuntu 如何使用 SCP 和 SFTP 安全传输文件

本文章向大家介绍Linux如何使用 SCP 和 SFTP 安全传输文件,主要内容包括使用 SCP 复制文件、使用 SFTP 复制文件、总结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这…

ChatGPT在音乐创作和生成中的应用如何?

ChatGPT在音乐创作和生成领域的应用非常有趣且多样化。虽然ChatGPT是一种自然语言处理模型,它并不是专门为音乐生成设计的,但它具有创造性和想象力,可以在一定程度上用于音乐创作。在音乐生成领域,有许多方法和技术可以将自然语言…

Kind | Kubernetes in Docker 把k8s装进docker!

有点像杰克船长的黑珍珠 目录 零、说明 一、安装 安装 Docker 安装 kubectl 安装 kind 二、创建/切换/删除集群 创建 切换 删除 将镜像加载到 kind 群集中 零、说明 官网:kind Kind: Kubernetes in Docker 的简称。kind 是一个使用 Docker 容…

用C语言写单片机驱动步进电机正反转角度程序

下面是一个简单的例子实现步进电机的正反转角度控制&#xff1a; c #include <reg51.h> #define MOTOR_PORT P1 // 步进电机的控制端口 // 定义正转和反转的步进电机序列 unsigned char forward_seq[4] {0x01, 0x02, 0x04, 0x08}; unsigned char backward_seq[4] …

CentOS5678 repo源 地址 阿里云开源镜像站

CentOS5678 repo 地址 阿里云开源镜像站 https://mirrors.aliyun.com/repo/ CentOS-5.repo https://mirrors.aliyun.com/repo/Centos-5.repo [base] nameCentOS-$releasever - Base - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$r…

数据仓库建设-数仓分层

数据仓库能够帮助企业做出更好的决策&#xff0c;提高业务效率和效益&#xff1b;在数据仓库建设时&#xff0c;绕不开的话题就是数仓分层。 一、数据分层的好处 1. 降低数据开发成本 通用的业务逻辑加工好&#xff0c;后续的开发任务可以基于模型快速使用&#xff0c;数据需…

数据库管理-第九十三期 19c OCM之路-第四堂(01)(20230719)

第九十三期 19c OCM之路-第四堂&#xff08;01&#xff09;&#xff08;20230719&#xff09; 距离上一期19c OCM之路已经过去了整整8天了&#xff0c;这中间发生的事情详见第九十二期。本期来到第四堂Performance management性能管理&#xff0c;但是一开始需要把上一堂的一些…

IDEA使用方式

1.翻译 1.Plugins插件&#xff1a;Chinese中文插件 文件F 编辑E 视图V 导航N 代码C 分析Z 重构R 构建B 运行U 工具T VCSS 窗口W 帮助H文件N 新建N 打开 打开最近 关闭项目 设置T 项目结构 文件属性 保存全部S 从磁盘全部重新加载 作废缓存/重启 导出/导入操作 其他设置 导出 打…

windows下mysql8定时备份,bat脚本编写,dos免密执行

前提&#xff1a;mysql8已经安装。 编写脚本copy_mysql_data.bat echo off set timestamp%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2% set backupfileD:\ProgramData\MySQL\Backup\backup_%timestamp%.sql set mysqlpathD:\Program Files\MySQL\MyS…

Django 常流程 MIDDLEWARE STATIC TIME DEBUG

目录 Django MIDDLEWARE 三步走 process_request process_view process_response Django STATIC_URL STATIC_ROOT STATICFILES_DIRS Django TIME_ZONE USE_TZ Django DEBUG Django MIDDLEWARE 三步走 process_request process_view process_response 在Django的中间件处理…

ubuntu版本Linux操作系统上安装键盘中文输入法

要在ubuntu版本Linux操作系统上安装键盘中文输入法 可以按照以下步骤进行操作&#xff1a; 1、Linux终端输入&#xff1a;sudo apt-get install ibus-pinyin 这将安装一个常用的中文输入法 “ibus-pinyin”。 2、重新启动系统&#xff1a;为了使输入法生效&#xff0c;需要…

微信小程序优化手段

提高小程序加载速度&#xff1a;小程序的加载速度对于用户体验有很大影响&#xff0c;可以通过减少文件大小、合并相同请求、使用 CDN 等方式来提升加载速度。 减少请求次数&#xff1a;过多的请求次数会降低小程序的性能表现&#xff0c;可以使用缓存、懒加载等技术来减少请求…

ubuntu打开usb摄像头

文章目录 前言一、识别 usb 摄像头二、安装应用程序显示摄像头捕捉到的视频1、使用应用程序茄子&#xff08;cheese&#xff09;2、运行 cheese 捕捉视频 前言 记录一下解决在 Linux 下打开 usb 摄像头界面黑屏的问题。 一、识别 usb 摄像头 1、保持在 ubuntu 界面&#xff0…

前端学习记录~2023.7.17~CSS杂记 Day9

前言一、浮动1、使盒子浮动起来2、清除浮动3、清除浮动元素周围的盒子&#xff08;1&#xff09;clearfix 小技巧&#xff08;2&#xff09;使用 overflow&#xff08;3&#xff09;display: flow-root 二、定位1、定位有哪些2、top、bottom、left 和 right3、定位上下文4、介绍…

科技赋能企业,实现数字化转型

科技是第一生产力&#xff0c;数字技术即科技&#xff0c;可以改变传统的商业模式&#xff0c;为各行各业注入新的活力。 推动企业数字化转型&#xff0c;可是实现行业的效率提升&#xff0c;实现跨界重组&#xff0c;重构产业模式&#xff0c;为产业格局重新赋能&#xff0c;最…

AJAX: 事件循环(举例细论)

概念&#xff1a;执行任务和收集异步任务&#xff0c;在调用栈空闲时&#xff0c;反复调用任务队列里回调函数的一种执行机制 原因&#xff1a;JavaScript 是单线程的&#xff0c;为了不阻塞 JS 引擎&#xff0c;设计执行代码的模型 JS内代码如何执行&#xff1a; 执行同步代…

PHP与Golang对战:两种语言的比较与应用场景探讨

引言 在软件开发领域&#xff0c;选择一种合适的编程语言对于项目的成功至关重要。而在今天的文中&#xff0c;我们将探讨两个备受争议的编程语言——PHP与Golang之间的对战。通过比较它们的优势和应用场景&#xff0c;帮助开发者更好地了解如何选择适合自己项目的语言。 PHP的…

伙伴云CEO戴志康:我们为什么要做伙伴云?

分享嘉宾&#xff1a;戴志康&#xff0c;伙伴云CEO 以下为演讲实录⬇⬇⬇ 01选择人更少的一条路&#xff0c;从B级走向A级 我一直想和大家交流一个话题&#xff0c;关于我们为什么要做伙伴云。既代表我自己&#xff0c;同时也代表我们团队的一些想法。 我是一个怀疑论者。大…

MyBatis第三讲:MyBatis初始化过程详解

MyBatis第三讲:MyBatis初始化过程详解 从上文我们知道MyBatis和数据库的交互有两种方式有Java API和Mapper接口两种,所以MyBatis的初始化必然也有两种;那么MyBatis是如何初始化的呢? 本文是MyBatis第三讲,详解MyBatis初始化过程。 1、MyBatis初始化的方式及引入 MyBatis的…

Win10 配置NDK安装2023.7.19版本

NDK安装流程 1. 下载&#xff1a;2. 安装&#xff1a;3. 测试&#xff1a; 在大多数情况下&#xff0c;使用 Android SDK 管理器安装 NDK 会更轻松。本文单独安装NDK&#xff0c;但后续也可以使用管理器进行管理。 1. 下载&#xff1a; 地址 Fig.1 最新稳定版本 2. 安装&…