硬核解析MySQL的MVCC实现原理,面试官看了都直呼内行

news/2024/5/5 18:37:17/文章来源:https://www.cnblogs.com/yidengjiagou/p/16663224.html

1. 什么是MVCC

MVCC全称是Multi-Version Concurrency Control(多版本并发控制),是一种并发控制的方法,通过维护一个数据的多个版本,减少读写操作的冲突。

如果没有MVCC,想要实现同一条数据的并发读写,还要保证数据的安全性,就需要操作数据的时候加读锁和写锁,这样就降低了数据库的并发性能。

有了MVCC,就相当于把同一份数据生成了多个版本,在操作的开始各生成一个快照,读写操作互不影响。无需加锁,也实现数据的安全性和事务的隔离性。

事务的四大特性中隔离性就是基于MVCC实现的。

说MVCC的实现原理之前,先说一下事务的隔离级别。

2. 事务的隔离级别

说隔离级别之前,先说一下并发事务产生的问题

脏读: 一个事务读到其他事务未提交的数据。

不可重复读: 相同的查询条件,多次查询到的结果不一致,即读到其他事务提交后的数据。

幻读: 相同的查询条件,多次查询到的结果不一致,即读到其他事务提交后的数据。

不可重复读与幻读的区别是: 不可重复读是读到了其他事务执行update、delete后的数据,而幻读是读到其他事务执行insert后的数据。

再说一下事务的四大隔离级别:

Read UnCommitted(读未提交): 读到其他事务未提交的数据,会出现脏读、不可重复读、幻读。

Read Committed(读已提交): 读到其他事务已提交的数据,解决了脏读,会出现不可重复读、幻读。

Repeatable Read(可重复读): 相同的条件,多次读取到的结果一致。解决了脏读、不可重复读,会出现幻读。

Serializable(串行化): 所有事务串行执行,解决了脏读、不可重复读、幻读。

隔离级别 脏读 不可重复读 幻读
读未提交
读已提交 不会
可重复读 不会 不会
串行化 不会 不会 不会

MVCC只在Read CommittedRepeatable Read两个隔离级别下起作用,因为Read UnCommitted隔离级别下,读写都不加锁,Serializable隔离级别下,读写都加锁,也就不需要MVCC了。

再谈一下Undo log日志。

3. Undo Log(回滚日志)

Undo Log记录的是逻辑日志,也就是SQL语句。

比如:当我们执行一条insert语句时,Undo Log就记录一条相反的delete语句。

作用:

  1. 回滚事务时,恢复到修改前的数据。

  2. 实现 MVCC

事务四大特性中原子性也是基于Undo Log实现的。

下面开始谈一下MVCC的实现原理。

4. MVCC的实现原理

4.1 当前读和快照读

先普及一下什么是当前读和快照读。

当前读: 读取数据的最新版本,并对数据进行加锁。

例如:insert、update、delete、select for update、 select lock in share mode。

快照读: 读取数据的历史版本,不对数据加锁。

例如:select

MVCC是基于Undo Log、隐藏字段、Read View(读视图)实现的。

4.2 隐藏字段

先说一下MySQL的隐藏字段,当我们创建一张表时,InnoDB引擎会增加2个隐藏字段。

DB_TRX_ID(最近一次提交事务的ID):修改表数据时,都会提交事务,每个事务都有一个唯一的ID,这个字段就记录了最近一次提交事务的ID。

DB_ROLL_PTR(上个版本的地址):修改表数据时,旧版本的数据都会被记录到Undo Log日志中,每个版本的数据都有一个版本地址,这个字段记录的就是上个版本的地址。

4.3 版本链

当我们第一次往用户表插入一条记录时,表数据和隐藏字段的值是下面这样的:

insert into user (name,age) values ('一灯',1);

事务ID(DB_TRX_ID)是1,上个版本地址(DB_ROLL_PTR)是null。

image

第二次提交事务,把用户年龄加1。

update user set age=age+1 where id=1;

事务ID变成2,上个版本地址指向Undo Log中的记录。

image

第三次提交事务,再把用户年龄加1。

update user set age=age+1 where id=1;

事务ID变成3,上个版本地址指向Undo Log中事务ID为2的记录。

image

这样表记录和Undo Log历史数据就组成了一个版本链。

4.4 Read View(读视图)

在事务中,执行SQL查询,就会生成一个读视图,是用来保证数据的可见性,即读到Undo Log中哪个版本的数据。

快照读一般是读取的历史版本的读视图,当前图会生成一个最新版本的读视图。

读视图是基于下面几个字段实现的:

m_ids :当前系统中活跃的事务ID集合,即未提交的事务。

min_trx_id :m_ids中最小的ID

max_trx_id :下一个要分配的事务ID

creator_trx_id: 当前事务ID

读视图决定当前事务能读到哪个版本的数据,从表记录到Undo Log历史数据的版本链,依次匹配,满足哪个版本的匹配规则,就能读到哪个版本的数据,一旦匹配成功就不再往下匹配。

数据可见性规则:

  1. DB_TRX_ID = creator_trx_id
    如果这个版本数据的事务ID等于当前事务ID,表示数据记录的最后一次操作的事务就是当前事务,当前读视图可以读到这个版本的数据。
  2. DB_TRX_ID < min_trx_id
    如果这个版本数据的事务ID小于所有活跃事务ID,表示这个版本的数据不再被事务使用,即事务已提交,当前读视图可以读到这个版本的数据。
  3. DB_TRX_ID >= max_trx_id
    如果这个版本数据的事务ID大于等于下一个要分配的事务ID,表示有新事务更新了这个版本的数据,这种情况下,当前读视图不可以读到这个版本的数据。
  4. min_trx_id <= DB_TRX_ID < max_trx_id
    如果这个版本数据的事务ID在当前系统中活跃的事务ID集合(m_ids)里面,表示这个版本的数据被其他事务更新过,当前读视图不可以读到这个版本的数据。
    如果这个版本数据的事务ID不在当前系统中活跃的事务ID集合(m_ids)里面,表示是在其他事务提交后创建的读视图,当前读视图可以读到这个版本的数据。

5. 不同隔离级别下可见性分析

在不同的事务隔离级别下,生成读视图的规则不同:

  • READ COMMITTED(读已提交) :在事务中每一次执行快照读时都生成一个读视图,每个读视图中四个字段的值都是不同的。
  • REPEATABLE READ(可重复读):仅在事务中第一次执行快照读时生成读视图,后续复用这个读视图。

5.1 READ COMMITTED(读已提交)

设置MySQL隔离级别为读已提交:

SET session TRANSACTION ISOLATION LEVEL READ COMMITTED;

image

执行两个事务,验证一下:

image

事务1第一次查询时,会生成一个读视图,读视图的各个属性如下:

属性
m_ids 1,2
min_limit_id 1
max_limit_id 3
creator_trx_id 1

可见的版本链数据是:

image

符号规则 DB_TRX_ID = creator_trx_id = 1,可以看到当前版本的数据。

事务1第二次查询时,会生成一个新的读视图,读视图的各个属性如下:

属性
m_ids 1
min_limit_id 1
max_limit_id 3
creator_trx_id 1

可见的版本链数据是:

image

符号规则 min_trx_id <= DB_TRX_ID < max_trx_id(1<=2<3),并且当前数据版本的事务ID不在当前系统中活跃的事务ID集合,可以看到当前版本的数据。

image

同一个事务内,相同的查询条件,查询到的数据不一致,查到了其他事务更新过的数据,也就是出现了不可重复读的情况。

再看一下,在可重复读隔离级别下,是怎么解决这个问题的。

5.2 REPEATABLE READ(可重复读)

设置MySQL隔离级别为可重复读:

SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;

image

执行两个事务,验证一下:

image

事务1第一次查询时,会生成一个读视图,读视图的各个属性如下:

属性
m_ids 1,2
min_limit_id 1
max_limit_id 3
creator_trx_id 1

可见的版本链数据是:

image

符号规则 DB_TRX_ID = creator_trx_id = 1,可以看到当前版本的数据。

事务1第二次查询时,会复用原有的读视图,读视图的各个属性如下:

属性
m_ids 1,2
min_limit_id 1
max_limit_id 3
creator_trx_id 1

可见的版本链数据是:

image

符号规则 min_trx_id <= DB_TRX_ID < max_trx_id(1<=2<3),并且当前数据版本的事务ID在当前系统中活跃的事务ID集合,所以是不可以看到当前版本的数据。

image

由此得知,可重复读隔离级别下,相同的查询条件,两次查询到的结果相同,也就是解决了可重复读的问题,是通过复用原有的读视图的方式解决的。

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

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

相关文章

Python + Django4 搭建个人博客(十二):实现删除文章功能页面

前文我们实现了分别实现了显示文章&#xff0c;创建文章等功能。 Python Django4 搭建个人博客&#xff08;十一&#xff09;: 利用表单实现创建文章的功能页面_李威威wiwi的博客-CSDN博客本篇我们学习了使用Django用于处理表单的Form类&#xff0c;利用forms.ModelForm 类的…

Linux Kernel TCP/IP Stack|Linux网络硬核系列

介绍Linux网络技术中最核心的部分--TCP/IP协议栈 。 我们先看一下抽象的网络协议栈模型 再按分层思想看Linux内核协议栈实现框架 1. socket layer socket框架 socket系统调用(socket,bind,listen,accept,send,recv等)BSD socket API协议栈sock抽象适配层tcp/udp/icmp/ra…

图论——欧拉回路

一、前置知识 1、连通、极大联通子图连通:图中任意两点皆可互达 极大连通子图:对连通图来说:是这个连通图本身 对非连通图来说: 有多个极大联通子图2、回路、简单回路、简单路径回路:从一个点到经过一些其他节点,再回到该点的一个路径。此时,除了起点和终点,其他节点也…

Python做一个中秋节嫦娥投食小游戏

山河远阔&#xff0c;烟火人间&#xff0c;又一年&#xff0c;千里婵娟~ 今天给大家带来的是给玉兔投喂月饼的小游戏。八月十五中秋夜晚&#xff0c;让我们对着月亮许愿&#xff1a;希望我们在意和在意我们的人&#xff0c;诸邪避退、百事无忌、平安喜乐、万事胜意。提前祝大家…

VMware Workstation虚拟机怎么和主机之间互传文件?

VMware Workstation虚拟机怎么和主机之间互传文件?前言 工具/材料 操作方法前言 在使用Windows 10工作时会遇到形形色色的问题,比如虚拟机需要与主机之间互传文件。那么如何进行设置呢?下面小编与你分享具体步骤和方法。 工具/材料 Windows 10操作系统 操作方法启动Windows …

前端JS-Day21

client系列:获得可视区域的相关信息clientWidth和offsetWidth区别:clientWidth只包含内容和padding,offsetWidth包含内容和内外边框。 立即执行函数:无需调用,直接执行。且独立创建了一个作用域。(function() {})(); (function(){}()); 两种写法 像素比:即devicePixe…

STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

STM32使用串口空闲中断&#xff08;IDLE&#xff09;和 DMA接收不定长数据 方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据&#xff0c;USART_IT_IDLE空闲中断是检测到有数据被接收后&#xff0c;总线上在一个字节的时间内没有再接…

spring cloud alibaba (一)

Spring Cloud Alibaba官网:https://spring.io/projects/spring-cloud-alibaba gitHub:https://github.com/alibaba/spring-cloud-alibaba这节主要目标:掌握nacos使用 了解服务与服务之间调用1、简介 1.1、什么是分布式 将一套系统拆分成不同子系统部署在不同服务器上(这叫分…

flex常用布局

公共样式:<style>* {margin: 0;padding: 0;}.has-flex {display: flex;}</style> 垂直居中 子元素左右分布 css.father-one {width: 100%;height: 200px;background-color: #fffcef;align-items: center; /*纵轴)方向上的对齐方式。*/justify-content: space-bet…

5.2 创建个人中心页面-前端部分

&#x1f60a;如果写的可以帮到你&#xff0c;可以点个赞吗&#xff0c;你的支持就是我写下去的动力。&#x1f60a; 本文阅读大概 10 分钟, 自己写加思考大概 1 ~ 2 小时。建议&#xff1a;代码可以手抄&#xff0c; 但不要复制。 1. 整体框架 2. 前端页面布局 使用 bootstra…

达梦数据库图形化工具

图形化工具列表 (1)DM数据库配置助手 (2)DM服务查看器 (3)DM管理工具 (4)DM控制台工具 (5)DM数据库迁移工具 (6)DM性能监测工具图形化工具详解 界面展示DM数据库配置助手[dmdba@localhost tool]$ ./dbca.sh DM服务查看器DM管理工具DM控制台工具DM数据库迁移工具DM性能监测工具功…

马拉车算法

这篇博客写的非常清晰 https://zhuanlan.zhihu.com/p/549242325 给定一个字符串,问有多少个以 k,f,c 结尾的回文子串。#include<bits/stdc++.h> using namespace std; #define lowbit(x) x&(-x) #define ll long long const int maxn=1e6+5; int n,sum; ll len[maxn…

22-09-04 西安 谷粒商城(01)MySQL主从复制、MyCat读写分离、MyCat分库分表

人人尽说江南好&#xff0c;游人只合江南老。 春水碧于天&#xff0c;画船听雨眠。 MySQL主从复制 mysql主从复制&#xff1a;分摊读写压力&#xff08;cpu计算压力&#xff09; 写交给主库&#xff0c;读有主从分摊处理&#xff08;原因是写操作较少&#xff0c;读操作较多&…

面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

由于现在大多计算机都是多核CPU,多线程往往会比单线程更快,更能够提高并发,但提高并发并不意味着启动更多的线程来执行。更多的线程意味着线程创建销毁开销加大、上下文非常频繁,你的程序反而不能支持更高的TPS。 时间片 多任务系统往往需要同时执行多道作业。作业数往往大…

ABAP-LP01和PDF打印机配置

事务代码SPAD1.LP01配置2.PDF配置

Netty+WebSocket整合STOMP协议

1.STOMP协议简介 常用的WebSocket协议定义了两种传输信息类型:文本信息和二进制信息。类型虽然被确定,但是他们的传输体是没有规定的,也就是说传输体可以自定义成什么样的数据格式都行,只要客户端和服务端约定好,得到数据后能够按照约定的语义解析数据就好。相较于Http协议…

猿创征文|我的后端成长之路(985科班两年,我发现了大学正确打开方式)

零.前言 当看到官方的这个活动的时候&#xff0c;我突然感到手指充满了力量&#xff0c;好像是我的键盘要向我尖端放电&#xff0c;谁还不是怀着满腔的热忱来写这篇文章帮助未来的学弟学妹们避坑呢&#xff1f;(其实是为了活动的奖励&#x1f917;)。不过不要在意这些细节&…

本地连接是干什么的?

当您创建家庭或小型办公网络时&#xff0c;运行 windows XP Professional 或 windows XP home edition 的计算机将连接到局域网 (Lan)。 安装 windows XP 时&#xff0c;将检测您的网络适配器&#xff0c;而且将创建本地连接。 像所有其他连接类型一样&#xff0c;它将出现在…

【深蓝学院】- Multiplane Images and Neural Rendering

01 view Synthesis problem Definition 02 View synthesis with multiplane Image(MPI) MPI的缺陷&#xff1a; 不是真正的三维表达不同视角观测的RGB是不变的 与MPI不一样的地方&#xff1a;不是手工设计的&#xff0c;而是整体输入 不同视角的RGB是不一样的 缺陷&#xff1…

Pytorch优化器全总结(一)SGD、ASGD、Rprop、Adagrad

目录 写在前面 一、 torch.optim.SGD 随机梯度下降 SGD代码 SGD算法解析 1.MBGD&#xff08;Mini-batch Gradient Descent&#xff09;小批量梯度下降法 2.Momentum动量 3.NAG(Nesterov accelerated gradient) SGD总结 二、torch.optim.ASGD随机平均梯度下降 三、torc…