MySQL 多版本并发控制 MVCC

news2024/12/23 0:24:07

MVCC出现背景

事务的4个隔离级别以及对应的三种异常

读未提交(Read uncommitted

读已提交(Read committed):脏读

可重复读(Repeatable read):不可重复读

串行化(Serializable):幻读

  • 脏读:一个事务读取到了另外一个事务没有提交的数据;
  • 不可重复读:在同一个事务中,两次读取同一数据,得到内容不同;
  • 幻读:同一事务中,用同样的操作读取两次,得到的记录数不同。

MySQL中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题,但不能解决幻读的问题。如果我们需要解决幻读的问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样依赖就会大幅降低数据库的事务并发能力。

MVCC就是通过乐观锁的方式来解决不可重复读和幻读的问题,它可以在大多数情况下替代行级锁,降低系统的开销。

MySQL并发事务会引起更新丢失问题,解决办法是锁,主要分两类:

  • 乐观锁

​ 其实就如它的名字一样,非常乐观,总是假设比较好的情况。

​ 每次取数据的时候都认为他人不会对其修改,所以不会上锁,但是会在更新的时候进行判断,看看在此期间内有没有人去更新这个数 据,可以使用版本号机制和CAS算法实现。

  • 悲观锁

​ 与乐观锁完全相反,非常悲观,总是假设非常糟糕的情况。

​ 每次取数据的时候都认为他人会对其进行修改,所以每次拿数据的时候都会加上锁,这样别人想拿数据的话就会阻塞,除非它拿到 锁。

什么是MVCC

Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。

多版本并发控制(MVCC) 是通过保存数据在某个时间点的快照来实现并发控制的,也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

简单来说,多版本并发控制的思想就是保存数据的历史版本,通过对数据行的多个版本管理来实现数据库的并发控制。这样我们就可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁也可以保证事务的隔离级别。

可以认为多版本并发控制是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销耕地。虽然实现机制有所不同,但大都实现类非阻塞的读操作,写操作也只锁定必要的行。

MySQL的大多数事务性存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制。不仅是MySQL,包括OraclePostgreSQL等其它数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准,典型的有乐观并发控制悲观并发控制

MVCC解决了什么问题

解决了在REPEATABLE READ和READ COMMITTED两个隔离级别下读同一行和写同一行的两个事务的并发。

  1. 读写直接阻塞的问题:通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

    提高并发的演进思路:

    • 普通锁,只能串行执行;
    • 读写锁,可以实现读读并发;
    • 数据多版本并发控制,可以实现读写并发。
  2. 降低了死锁的概率:因为 InnoDBMVCC采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

  3. 解决一致性读的问题:一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

举个简单的例子:

  1. 一个事务AtxnId=100),修改了数据X,使得X=1,并且commit了。
  2. 另外一个事务BtxnId=101)开始尝试读取X,但是还X=1。但B没有提交。
  3. 第三个事务CtxnId=102)修改了数据X,使得X=2,并且提交了。
  4. 事务B又一次读取了X。这时
    • 如果事务BRead Committed,那么就读取X的最新commit的版本,也就是X=2。
    • 如果事务B是Repeatable Read。那么读取的就是当前事务(txnId=101)之前X的最新版本,也就是XtxnId=100提交的版本,即X=1。

注意,这里B不论是Read Committed,还是Repeatable Read,都不会被锁,都能立刻拿到结果。这也就是MVCC存在的意义。

快照读与当前读

快照读SnapShot Read)是一种一致性不加锁的读,是InnoDB并发如此之高的核心原因之一。

这里的一致性是指,事务读取到的数据,要么是事务开始前就已经存在的数据,要么是事务自身插入或者修改过的数据

不加锁的简单SELECT属于快照读,例如:

select * from users where id = '1';

快照读相对应的则是当前读当前读就是读取最新数据,而不是历史版本的数据。加锁的SELECT就属于当前读,例如:

select * from users where id = '1' lock in share mode;
select * from users where id = '1' for update;

MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读,而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现

InnoDB的MVCC是如何工作的

当查询一条记录的时候,执行流程如下:

  1. 首先获取事务自己的版本号,也就是事务 ID
  2. 获取 Read View
  3. 查询得到的数据,然后与 Read View 中的事务版本号进行比较;
  4. 如果不符合 Read View 规则,就需要从 Undo Log 中获取历史快照;
  5. 最后返回符合规则的数据。

InnoDB是如何储存记录多个版本的

事务版本号

每开启一个事务,我们都会从数据库中获取一个事务ID(也就是事务版本号),这个事务ID是自增长的,通过ID大小,我们就可以判断事务的时间顺序。

行记录的隐藏列

InnoDB的叶子段储存了数据页,数据页中保存了行记录,而在行记录中有一些重要的隐藏字段

  • DB_ROW_ID6-byte,隐藏的行ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时InnoDB就会用这个隐藏ID来创建聚簇索引。采用聚簇索引的方式可以提升数据的查找效率。
  • DR_TRX_ID6byte,操作这个数据的事务ID,也就是最后一个对该数据进行插入或更新的事务ID
  • DB_ROLL_PTR7byte,回滚指针,也就是指向这个记录的Undo log信息。

InnoDB数据记录隐藏列

Undo Log

InnoDB将行记录保存在了Undo Log,我们就可以在回滚段中找到它们,如下图所示:

Undo Log回滚历史记录

从图中能看到回滚指针将数据行的所有快照记录都通过链表的结构串联了起来,每个快照的记录都保存了当时的db_trx_id,页就是那个时间点操作这个数据的事务ID。这样如果我们想找历史数据快照,就可以通过遍历回滚指针的方式进行查找。

Read View(读视图)

什么是Read View,说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

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

  • trx_list 未提交事务ID列表,用来维护Read View生成时刻系统正活跃的事务ID
  • up_limit_id 记录trx_list列表中事务ID最小的ID
  • low_limit_id ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
  • 首先比较DB_TRX_ID < up_limit_id, 如果小于,则当前事务能看到DB_TRX_ID 所在的记录,如果大于等于进入下一个判断
  • 接下来判断 DB_TRX_ID 大于等于 low_limit_id, 如果大于等于则代表DB_TRX_ID 所在的记录在Read View生成后才出现的,那对当前事务肯定不可见,如果小于则进入下一个判断
  • 判断DB_TRX_ID 是否在活跃事务之中,trx_list.contains(DB_TRX_ID),如果在,则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的;如果不在,则说明,你这个事务在Read View生成之前就已经Commit了,你修改的结果,我当前事务是能看见的

在可重复读(REPEATABLE READ)隔离级别下,InnoDBMVCC是如何工作的

查询

InnoDB会根据以下两个条件检查每行记录:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样确保事务读取的行,要么实在事务开始前已经存在的,要么是事务自身插入或者修改过的
  2. 行的删除版本要么未定义,要么大于当前事务版本号。这样可以确保事务读取到的行,在事务开始之前未被删除
插入

InnoDB未新插入的每一行保存当前系统号作为行版本号。

删除

InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
删除在内部被视为更新,行中的一个特殊位会被设置为已删除。

更新

InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

参考文章

MySQL - MySQL InnoDB的MVCC实现机制

MySQL的多版本并发控制(MVCC)是什么?

值得收藏,揭秘 MySQL 多版本并发控制实现原理

MVCC解决了什么问题?

MVCC实现原理之ReadView(一步到位)

MVCC百度百科

脏读、不可重复读的最终解决方案——MVCC

RR有幻读问题吗?MVCC能否解决幻读?

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

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

相关文章

最简单爱心的解析

首先你需要了解爱心代码在直角坐标系的方程 数学知识&#xff1a;x 属于 -1.5 ~ 1.5 y 属于 -1 ~ 1.5 和 高中所学的线性规划 请看代码 #include <math.h> #include <stdlib.h> #include <Windows.h> #include <stdio.h> int main() { …

java.lang.UnsupportedOperationException: null 其一解决办法

文章目录 前言一、错误回顾1.详细信息2.代码详情 二、解决方案1.错误原因2.解决方案1.使用 new ObjectMapper() new TypeReference<List>(){}2.使用 SerializerFeature.WriteMapNullValue.getMask() 总结 前言 当我们远程调用传递泛型集合&#xff0c;如 List<?>…

Access数据库模糊查询

Access数据库模糊查询的方法 1.使通配符: 在 Access 数据库中&#xff0c;有两种通配符可供使用&#xff0c;分别是"?"和"*"。 "?"表示匹配一个字符。 "*"表示匹配任意多个字符 1例如&#xff1a;如果要查询姓氏以"王"开…

MySQL/Oracle 的 字符串拼接

目录 MySQL、Oracle 的 字符串拼接1、MySQL 的字符串拼接1.1 CONCAT(str1,str2,...) : 可以拼接多个字符串1.2 CONCAT_WS(separator,str1,str2,...) : 指定分隔符拼接多个字符串1.3 GROUP_CONCAT(expr) : 聚合函数&#xff0c;用于将多行的值连接成一个字符串。 2、Oracle 的字…

一个超级牛逼的消息推送系统Gotify 使用Gotify来搭建你的消息推送系统

目录 先看效果 简介 1.1创建目录 3.访问服务端 3.1示例 3.2创建应用 4.安装apk 4.1下载apk 4.2安装 4.3配置服务器地址 5.推送消息测试 5.1服务器执行 5.2手机端查看 支持删除 6.源码地址 先看效果 打开应用 简介 gotify 支持的功能如下 可以通过 restapi 发送消…

mysql新增用户密码控制局域网访问权限

方法一、通过navicat中sql语句新增 CREATE USER usernamelocalhost IDENTIFIED BY password; GRANT ALL PRIVILEGES ON *.* TO usernamelocalhost WITH GRANT OPTION; FLUSH PRIVILEGES;把其中的username和password改成自己的即可 如果将上面的localhost改成%&#xff0c;则这…

flash-attn库安装记录

flash-attn库安装记录 第一步&#xff1a; 安装好cuda11.7 第二步&#xff1a; 使用代码export CUDA_HOME/usr/local/cuda-11.7让库找到cuda路径 第三步&#xff1a; 使用pip install flash-attn --no-build-isolation安装 安装成功显示

使用python将图像复刻到excel表格

上班摸鱼的时候突然想到以前有个日本老爷爷用excel表格作画感觉挺牛的&#xff0c; 然后想到图像可以分割成一个个小块&#xff0c;excel表格也是一个个小格子&#xff0c;将小块的坐标和颜色对应填充到表格中&#xff0c;不就行了。 效果如下&#xff1a; 完整代码&#xff1…

手把手教你用jmeter做压力测试(详图)

一.前言 压力测试是每一个Web应用程序上线之前都需要做的一个测试&#xff0c;他可以帮助我们发现系统中的瓶颈问题&#xff0c;减少发布到生产环境后出问题的几率&#xff1b;预估系统的承载能力&#xff0c;使我们能根据其做出一些应对措施。所以压力测试是一个非常重要的步…

windows server 2019 云服务器看不见硬盘的解决方案

刚拿的windows server 服务器看不见硬盘&#xff0c;这是因为没有初始化数据盘的原因。 解决方案如下&#xff1a; 单击“服务器管理器”仪表盘。 弹出“服务器管理器”窗口&#xff0c;如图1所示。 “服务器管理器”页面右上方选择“工具 > 计算机管理”。 弹出“计算机管…

Vue:将以往的JQ页面,重构成Vue组件页面(组件化编码大致流程)

一、实现静态组件 组件要按照功能点拆分&#xff0c;命名不要与HTML元素冲突。 1、根据UI提供的原型图&#xff0c;进行结构设计&#xff0c;结构设计的粒度以是否方便给组件起名字为依据。并梳理好对应组件的层级依赖关系。 2、设计好结构后&#xff0c;开始写对应的组件&am…

vue2使用electron以及打包配置

1.创建项目 vue create vue-project 2.安装electron vue add electron-builder会自动安装相关依赖 安装成功后会在src下自动生成一个background.js文件就是相应的electron的配置信息 use strictimport { app, protocol, BrowserWindow } from electron import { createProto…

【书生·浦语】大模型实战营——第四课作业

教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/main/xtuner/self.md 基础作业需要构建数据集&#xff0c;微调模型&#xff0c;让其明白自己的弟位&#xff08;OvO&#xff01;&#xff09; 微调环境准备 进入开发机后&#xff0c;先bash&#xff0c;再创…

P9847 [ICPC2021 Nanjing R] Crystalfly 题解 (SPJ)

[ICPC2021 Nanjing R] Crystalfly 传送门&#xff1f; 题面翻译 给定一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le10^5) n(1≤n≤105) 个节点的树&#xff0c;每个节点上有 a i a_i ai​ 只晶蝶。派蒙最初在 1 1 1 号节点&#xff0c;并获得 1 1 1 号节点的所有晶蝶&#xf…

vba设置excel单元格背景色

vba设置excel单元格背景色位蓝色 Sheet1.Cells(hang, 2).Interior.Color RGB(0, 0, 255) 参考链接 【VBA】给单元格设置背景色_vba 将一行底色置绿色-CSDN博客https://blog.csdn.net/s_h_m114_2/article/details/105787093 参考2 知乎 VBA--单元格的背景色设置 特此…

如何实现固定公网地址同步Zotero科研文献管理器

文章目录 一、Zotero安装教程二、群晖NAS WebDAV设置三、Zotero设置四、使用公网地址同步Zotero文献库五、使用永久固定公网地址同步Zotero文献库 Zotero 是一款全能型 文献管理器,可以 存储、管理和引用文献&#xff0c;不但免费&#xff0c;功能还很强大实用。 ​ Zotero 支…

MTK平台-- 如何学习Bluedroid 的GATT

一 GATT定义 GATT是低功耗蓝牙属性应用规范,应用于主机和从设备之间的数据传输。 二 MTK平台Bluedroid框架 相关源码介绍 vendor/mediatek/proprietary/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java (用于GATT(Generic Attri…

解决方案:reactNative通过webview跳转微信智能客服空白webview页面

在reactNative中使用webview跳转微信智能客服&#xff0c;功能正常&#xff0c;从微信退回到App时&#xff0c;会有一个空白的webview页面&#xff0c;在使用感觉上不是那么的顺滑。解决这个可以在webview中使用onLoadEnd方法来解决这个问题 在react-native-webview中onLoadEn…

HackTheBox - Medium - Linux - Mentor

Mentor Mentor 是一台中等难度的 Linux 机器&#xff0c;其路径包括在到达 root 之前在四个不同的用户之间切换。使用可暴力破解的社区字符串扫描“SNMP”服务后&#xff0c;会发现用于“API”端点的明文凭据&#xff0c;该端点被证明容易受到盲目远程代码执行的影响&#xff…

使用WAF防御网络上的隐蔽威胁之命令注入攻击

命令注入攻击是网络安全领域的一种严重威胁&#xff0c;它允许攻击者在易受攻击的应用程序上执行恶意命令。 这种攻击通常发生在应用程序将用户输入错误地处理为操作系统命令的情况下。 什么是命令注入攻击 定义&#xff1a;命令注入攻击发生在攻击者能够在易受攻击的应用程…