MySql MVCC 详解

注意以下操作都是以InnoDB引擎为操作基准。

一,前置知识准备

1,MVCC简介

MVCC 是多版本并发控制(Multiversion Concurrency Control)的缩写。它是一种数据库事务管理技术,用于解决并发访问数据库的问题。MVCC 通过创建多个版本的同一数据,每个版本与一个事务关联,来实现并发控制。

数据库在执行更新操作时,会保留之前版本的数据,以便其他正在执行事务的用户可以访问这些数据。每个事务都能看到一个稳定的数据快照,并且仅接触到他们自己的版本,这意味着每个事务可以独立地读取和写入数据,而不会干扰其它事务。

MVCC 在数据库的可伸缩性和性能方面具有重要作用,尤其是对于高并发的应用程序,如电子商务网站和社交媒体应用。

2,MySQL的逻辑架构

第一层:处理客户端连接、授权认证,安全校验等。

第二层:服务器server层,负责对SQL解释、分析、优化、执行操作引擎等。

第三层:存储引擎,负责MySQL中数据的存储和提取。
在这里插入图片描述

3,事务的四大特性

ACID是指数据库事务所必须具备的四个特性,包括:

  1. 原子性(Atomicity):事务是一个不可分割的原子操作,要么全部执行成功,要么全部失败回滚,不允许出现部分执行成功或失败的情况。【undolog】

  2. 一致性(Consistency):事务执行前和执行后,数据库的完整性约束没有被破坏,也就是说,在事务完成后,数据库从一个一致性状态转变为另一个一致性状态。

  3. 隔离性(Isolation):事务之间是相互隔离的,一个事务的执行不能被其他事务干扰。多个事务并发执行时,它们之间的执行是独立的,每个事务感觉就像是在独占数据库。【锁,mvcc】

  4. 持久性(Durability):事务完成后,对于数据的修改是永久性的,即使系统故障也不会导致数据的丢失。因此,数据库中所有的数据修改都需要记录到日志中,以便在系统故障恢复时进行重做。【redolog】

4,事务的四大隔离级别

事务的隔离级别是指多个事务同时操作一个数据库时,数据库系统对这些事务分配的隔离程度。在不同的隔离级别下,事务之间的隔离程度也不同。常见的隔离级别包括:

  1. 读未提交(Read Uncommitted):允许一个事务读取另一个事务未提交的数据。这种隔离级别对数据的完整性和一致性影响较大。【存在脏读】

  2. 读已提交(Read Committed):一个事务只能读取已经提交的数据,其他未提交的事务所做的修改对它不可见。这种隔离级别对数据完整性和一致性的保护较好。【解决脏读,存在不可重复读】【mysql InnoDB 通过 mvcc 解决 】

  3. 可重复读(Repeatable Read):一个事务在执行期间看到的数据是固定的,不受其他并发事务的影响。即使其他事务已经提交了对数据的修改,当前事务也只能看到自己在读取数据时的版本。这种隔离级别对数据的完整性和一致性有一定保护。【解决不可重复读,存在幻读】【mysql InnoDB 通过 mvcc 解决 】

  4. 串行化(Serializable):最高的隔离级别,确保事务之间的操作是完全独立的,一个事务的操作必须等另一个事务结束后才能开始。这种隔离级别对数据完整性和一致性的保护最好,但并发性能较低。【解决脏读,解决不可重复读,解决幻读】【mysql InnoDB 通过 加锁解决 】

在实际应用中,应根据业务需求和系统性能等因素选择合适的隔离级别。

5,mysql 常用的日志

server 层

  1. 错误日志(Error Log): 记录 MySQL 服务器启动、运行过程中出现的重要错误和警告信息。
  2. 慢查询日志(Slow Query Log): 记录执行时间超过预设时间的查询语句,通常用于优化查询性能。
  3. 二进制日志(Binary Log): 记录数据库更新操作,用于主从复制和数据恢复。
  4. 中继日志(Relay Log): 只在主从复制时使用,记录从服务器复制主服务器二进制日志的过程。

引擎层

  • 重做日志(Redo log): 是一种用于恢复数据库中未提交和已提交事务的机制。其包含了所有已经被写入到磁盘上的事务,通常被存储在磁盘上的一组文件中。当数据库系统宕机或者发生崩溃时,可以通过redo log来恢复数据库并且保证ACID属性。
  • 回滚日志(Undo log): 则用于撤销已经提交或者未提交的事务。它记录了事务执行前的数据,以便当出现错误时可以将数据回滚到事务执行前的状态。在数据库中,undo log通常被用于避免脏读、不可重复读和幻读等问题。

二,mvcc 原理

1,当前读和快照读

在学习 MVCC 多版本并发控制之前,我们必须先了解一下,什么是 MySQL InnoDB 下的当前读和快照读。

  • 当前读,像 select lock in share mode (共享锁), select for update; update;insert; delete (排他锁)这些操作都是一种当前读,为什么叫当前读 ? 就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

  • 快照读,像不加锁的 select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读,之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC.可以认为 MVCC 是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。
    在这里插入图片描述

在InnoDB中的每一条记录实际都会存在三个隐藏列

  • DB_TRX_ID:事务 ID,是根据事务产生时间顺序自动递增的,是独一无二的。如果某个事务执行过程中对该记录执行了增、删、改操作,那么InnoDB存储引擎就会记录下该条事务的 id。

  • DB_ROLL_PTR:回滚指针,本质上就是一个指向记录对应的undo log的一个指针,InnoDB 通过这个指针找到之前版本的数据

  • DB_ROW_ID:主键,如果有自定义主键,那么该值就是主键;如果没有主键,那么就会使用定义的第一个唯一索引;如果没有唯一索引,那么就会默认生成一个隐藏列作为主键。
    在这里插入图片描述

2,undolog

这些数据快照都是存储在undolog 中的,这些数据分为两类

  • Insert undo log :insert生成的日志,仅在事务回滚中需要,并且可以在事务提交后立即丢弃。

  • Update undo log:update/delete生成的日志,除了用于事务回滚,还用于一致性读取,只有不存在innodb为其分配快照的事务之后才能丢弃它们,在一致读取中可能需要update undo log中的信息来构建数据库行的早期版本。

删除操作实际上不会直接删除,而只是标记为删除,最终的删除操作是purge线程完成的

purge线程作用,一是清理undo log,二是清除page里面带有Delete_Bit标识的数据行。在InnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识删除,真正的删除工作需要后台purge线程去完成。

3,Read View(读视图)

事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照。

记录并维护系统当前活跃事务的ID(trx_id)(没有commit,当每个事务开启时,都会被分配一个ID,
这个ID是递增的,所以越新的事务,ID值越大),是系统中当前不应该被本事务看到的其他事务id列表。

Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read
View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo
log里面的某个版本的数据。

Read View几个属性
在这里插入图片描述

MVCC 只在 Read Commited(读已提交) 和 Repeatable Read(可重读读) 两种隔离级别下工作。

  • 在RC隔离级别下,是每个快照读都会生成并获取最新的Read View,这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因。

  • 在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View,从而做到可重复读。

4,可见性算法

1、首先比较DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断。

2、接下来判断DB_TRX_ID >= low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在Read View生成后才出现的,那么对于当前事务肯定不可见,如果小于,则进入下一步判断。

3、判断DB_TRX_ID是否在活跃事务中,如果在,则代表在Read View生成时刻,这个事务还是活跃状态,还没有commit,修改的数据,当前事务也是看不到,如果不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看见的。

5,mvcc能否解决幻读
在一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读。而多出来或者少的哪一行被叫做幻行。

在快照读读情况下,mysql通过mvcc来避免幻读。

在当前读读情况下,mysql通过next-key来避免幻读。

不能把快照读和当前读得到的结果不一样这种情况认为是幻读,这是两种不同的使用。所以mysql的RR级别是解决了幻读的。

三,mvcc 场景验证

所谓光说不练假把式,光练不说傻把式,又练又说真把式,上面说的只是结论,下面进行实际的操作演示。

mysql 版本 5.7

创建测试表

CREATE TABLE `user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`age` tinyint(3) DEFAULT NULL,`gender` tinyint(1) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_abc` (`name`,`age`,`gender`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

(开始之前将事务自动提交关闭)

1,场景一

当前的事务隔离级别为,可重复读(Repeatable Read)
在这里插入图片描述

步骤一,t1时刻同时开启事务

步骤二,t2时刻事务2更新并提交

步骤三,t3时刻事务1查询

问题,事务1在t3时刻是否能查询t2时刻事务2提交的数据。

预期结果能

因为执行的是select所以为快照读,但是由于Read View是在快照读的时候才产生,并且可重复读(Repeatable
Read)隔离级别下,只产生一个Read
View。事务2更新并提交完成时候,由于此时事务1还没进行快照读。所以此时事务1查询的时候是可以查询到事务2更新并提交后的数据的。

sql 执行验证
在这里插入图片描述

查询结果符合预期

通过算法核对

trx_list:1,3 【当前活跃事务为 1,3,因为2已经提交了】

low_limit_id:4【当前系统最大事务版本号+1,因为已经开启了3个事务,所以下一个应该是4】

up_limit_id:1 【创建当前read view 时“系统正处于活跃事务最小版本号,活跃的只有1,3,最小的是1】
在这里插入图片描述

算法验证:验证当前事务1是否能看到事务2提交的数据。

(1)DB_TRX_ID < up_limit_id 【2<1】不成立继续判断

(2)DB_TRX_ID >= low_limit_id 【2>=4】不成立继续判断

(3)判断DB_TRX_ID是否在活跃事务中,不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看见的。

2,场景二

当前的事务隔离级别为,可重复读(Repeatable Read)
在这里插入图片描述

步骤一,t1时刻同时开启事务

步骤二,t2时刻事务1进行快照读

步骤三,t3时刻事务2修改并提交

步骤四,t4时刻事务1进行快照读

问题,事务1在t4时刻是否能查询t3时刻事务2提交的数据。

预期结果不能

事务1在t2时刻执行的是select所以为快照读,此时Read View产生,并且可重复读(Repeatable
Read)隔离级别下,只产生一个Read View。事务2在t3更新并提交完成时候,由于此时事务1已经产生了Read
View,再次进行快照读。所以此时事务1查询的时候是可以查询不到事务2更新并提交后的数据的,因为读取的是快照中的数据。

在这里插入图片描述

sql 执行验证

查询结果符合预期

通过算法核对

第一次查询

trx_ids:1,2,3 【当前活跃事务为 1,2,3,此时事务2还没开始修改提交】

low_limit_id:4【当前系统最大事务版本号+1,因为已经开启了3个事务,所以下一个应该是4】

up_limit_id:1 【创建当前read view 时“系统正处于活跃事务最小版本号,活跃的只有1,3,最小的是1】

第二次查询

可重复读(Repeatable Read)事务隔离级别下只产生一个read view

trx_ids:1,3 【当前活跃事务为 1,3,此时事务2已经修改提交】

low_limit_id:4【当前系统最大事务版本号+1,因为已经开启了3个事务,所以下一个应该是4】

up_limit_id:1 【创建当前read view 时“系统正处于活跃事务最小版本号,活跃的只有1,3,最小的是1】

在这里插入图片描述

第二次快照读的时候,当前数据的DB_TRX_ID为2

(1)DB_TRX_ID < up_limit_id 【2<1】不成立继续判断

(2)DB_TRX_ID >= low_limit_id 【2>=4】不成立继续判断

(3)判断DB_TRX_ID是否在活跃事务中,在,则代表在Read View生成时刻,这个事务还是活跃状态,还没有commit,修改的数据,当前事务也是看不到。

3,场景三

当前的事务隔离级别为,可重复读(Repeatable Read)

如果事务中全部都是快照读,不会产生幻读问题,但是当快照读和当前读一起使用的时候就会产生幻读问题。

Mysql的解决方案是加锁,想要解决这个问题,必须要保证当前读和快照读的数据必须要一致,只能去阻止其他事务进行插入操作,所以只能加锁。

sql 执行验证

1,如果事务中全部都是快照读,不会产生幻读问题

在这里插入图片描述

2,当快照读和当前读一起使用的时候就会产生幻读问题
在这里插入图片描述

3,如果事务中全部都是当前读,不会产生幻读问题【加锁实现的】

在这里插入图片描述

提交后立刻执行
在这里插入图片描述

4,场景四

当前的事务隔离级别为,读已提交(Read Committed)

验证在此隔离级别,每一次快照读都会产生一次新的read view
在这里插入图片描述

步骤一,t1时刻同时开启事务

步骤二,t2时刻事务1进行快照读

步骤三,t3时刻事务2修改并提交

步骤四,t4时刻事务1进行快照读

问题,事务1在t4时刻是否能查询t3时刻事务2提交的数据。

预期结果能

事务1在t2时刻执行的是select所以为快照读,此时Read View产生,读已提交(Read
Committed)隔离级别下,产生多个Read
View。所以事务2在t3更新并提交完成时候,事务1在t4时刻再次进行快照读。此时又产生了新的Read View。新的Read
View是在t3时刻事务2修改并提交产生的所以可以查看到。

sql 验证
在这里插入图片描述

符合预期结果

通过算法核对

第一次查询

trx_ids:1,2,3 【当前活跃事务为 1,2,3,此时事务2还没开始修改提交】

low_limit_id:4【当前系统最大事务版本号+1,因为已经开启了3个事务,所以下一个应该是4】

up_limit_id:1 【创建当前read view 时“系统正处于活跃事务最小版本号,活跃的只有1,3,最小的是1】

第二次查询

可重复读(Repeatable Read)事务隔离级别下只产生一个read view

trx_ids:1,3 【当前活跃事务为 1,3,此时事务2已经修改提交】

low_limit_id:4【当前系统最大事务版本号+1,因为已经开启了3个事务,所以下一个应该是4】

up_limit_id:1 【创建当前read view 时“系统正处于活跃事务最小版本号,活跃的只有1,3,最小的是1】

在这里插入图片描述

第二次快照读的时候,当前数据的DB_TRX_ID为2

(1)DB_TRX_ID < up_limit_id 【2<1】不成立继续判断

(2)DB_TRX_ID >= low_limit_id 【2>=4】不成立继续判断

(3)判断DB_TRX_ID是否在活跃事务中,不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看见的。

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

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

相关文章

GUI的简单概述和基本使用

GUI的概念 1&#xff0c;到目前为止&#xff0c;我们编写的都是控制输入的程序&#xff0c;操作使用非常不直观&#xff0c;采取一直方式让效果呈现在窗口上。 2&#xff0c;GUI及图形界面指采用图像方式显示的用户界面&#xff0c;与早期计算机的命令行界面相比&#xff0c;…

【SpringCloud】通过Redis手动更新Ribbon缓存来解决Eureka微服务架构中服务下线感知的问题

文章目录 前言1.第一次尝试1.1服务被调用方更新1.2压测第一次尝试1.3 问题分析1.4 同步的不是最新列表 2.第二次尝试2.1调用方过滤下线服务2.2压测第二次尝试2.3优化 写到最后 前言 在上文的基础上&#xff0c;通过压测的结果可以看出&#xff0c;使用DiscoveryManager下线服务…

程序员如何开发高级python爬虫?

之前我有写过一篇“高级爬虫和低级爬虫的区别”的文章&#xff0c;我们知道它并非爬虫领域中专用术语。只是根据爬虫的复杂性来断定是否是高级爬虫。以我个人理解&#xff1a;高级爬虫是可能具有更复杂的功能和更高的灵活性的爬虫。下面我们围绕高级爬虫来了解下有趣的事情。 低…

python可视化plotly 图例(legend)设置大全,值得收藏!

文章目录 一、图例(legend)二、update\_layout(legend{}) 相关参数及示例关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python…

多向通信----多人聊天

package 多人聊天; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; publ…

K8s中安装calico一直无法启动成功

问题描述 #k8s版本为v1.20.9查看对应日志 #calico-node-xxx 对应pod名称 kubectl logs calico-node-xxxx -n kube-system #没有错误但是一直启动不起来应该是版本不匹配问题解决方案 删除 calico重新安装对应版本删除podskubectl delete -f calico.yaml删除文件 rm -f calico.…

【NLP】如何管理大型语言模型 (LLM)

什么是LLM编排&#xff1f; LLM 编排是管理和控制大型语言模型 (LLM)的过程&#xff0c;以优化其性能和有效性。这包括以下任务&#xff1a; 提示LLM&#xff1a;生成有效的提示&#xff0c;为LLMs提供适当的背景和信息以产生所需的输出。链接LLM&#xff1a; 结合多个LLM的输…

SpringBoot读取properties文字乱码问题及相关问题

问题&#xff1a;在idea的编辑器中properties文件一般用UTF-8编码&#xff0c;SpringBoot2读取解码方式默认不是UTF-8&#xff0c;当值出现中文时SpringBoot读取时出现了乱码。 解决方式1&#xff1a;在SpringBoot框架层面解决&#xff0c;在配置类注解上添加encoding属性值为…

5G - NR物理层解决方案支持6G非地面网络中的高移动性

文章目录 非地面网络场景链路仿真参数实验仿真结果 非地面网络场景 链路仿真参数 实验仿真结果 Figure 5 && Figure 6&#xff1a;不同信噪比下的BER和吞吐量 变量 SISO 2x2MIMO 2x4MIMO 2x8MIMOReyleigh衰落、Rician衰落、多径TDL-A(NLOS) 、TDL-E(LOS)(a)QPSK (b)16…

[足式机器人]Part4 南科大高等机器人控制课 Ch02 Rigid Body Configuration and Velocity

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;CLEAR_LAB 笔者带更新-运动学 课程主讲教师&#xff1a; Prof. Wei Zhang 南科大高等机器人控制课 Ch02 Rigid Body Configuration and Velocity 1. Rigid Body Configuration1.1 Special Orthogonal Group1.2 Use of Ro…

Centos7安装宝塔面板8.0.3并实现公网远程登录宝塔面板【内网穿透】

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔&#xff0c;内网穿透三、使用固定公网地址访问宝塔 宝塔面板作为建站运维工具&#xff0c;适合新手&#xff0c;简单好用。当我们在家里/公司搭建了宝塔&#xff0c;没有公网IP&#xff0c;但是想要在外也可以访问内…

组合总和II(回溯、去重)

40. 组合总和 II - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a…

相交链表(LeetCode 160)

文章目录 1.问题描述2.难度等级3.热门指数4.解题思路方法一&#xff1a;暴力法方法二&#xff1a;哈希表方法三&#xff1a;双栈方法四&#xff1a;双指针&#xff1a;记录链表长度方法五&#xff1a;双指针&#xff1a;互换遍历 5.实现示例参考文献 1.问题描述 给两个单链表的…

Java第二十一章网络通信

一、网络程序设计基础 1、局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机&#xff0c;如下图所示。 2、网络协议 1.IP协议 IP指网际互连协议&#xff0c;Internet Protocol的缩写&#xff0c;是TCP/IP体系中的网络层协议。设计IP的目的…

深入理解数据在内存中是如何存储的,位移操作符如何使用(能看懂文字就能明白系列)文章超长,慢慢品尝

系列文章目录 C语言笔记专栏 能看懂文字就能明白系列 &#x1f31f; 个人主页&#xff1a;古德猫宁- &#x1f308; 信念如阳光&#xff0c;照亮前行的每一步 文章目录 系列文章目录&#x1f308; *信念如阳光&#xff0c;照亮前行的每一步* 前言引子一、2进制和进制转化为什么…

Verilog开源项目——百兆以太网交换机(四)令牌桶管理单元设计

Verilog开源项目——百兆以太网交换机&#xff08;四&#xff09;令牌桶管理单元设计 &#x1f508;声明&#xff1a;未经作者允许&#xff0c;禁止转载 &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f511;全新原创以太网交换机项目&#xff0c;Blog内容将聚焦…

前缀和|二分查找|LeetCode2234| 花园的最大总美丽值

作者推荐 贪心算法LeetCode2071:你可以安排的最多任务数目 本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 二分查找算法合集 题目 Alice 是 n 个花园的园丁&#xff0c;她想通过种花&#xff0c;最大化她所有花…

pycharm中debug,py文件

1、先把需要的实参传入 2、在合适位置打上断点 3、在小三角旁边右键调用调试 4.步进/步出查看 5.选择单步执行&#xff0c;走的更慢

使用Python实现爬虫IP负载均衡和高可用集群

做大型爬虫项目经常遇到请求频率过高的问题&#xff0c;这里需要说的是使用爬虫IP可以提高抓取效率&#xff0c;那么我们通过什么方法才能实现爬虫IP负载均衡和高可用集群&#xff0c;并且能快速的部署并且完成爬虫项目。 通常在Python中实现爬虫ip负载均衡和高可用集群需要一…

基于ssm助学贷款网站论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本助学贷款管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…