【SQL】MVCC 多版本并发控制

news2024/9/22 2:06:43

MVCC多版本并发控制

  • 快照读与当前读
  • 隔离级别
  • 隐藏字段,undo log 版本链
    • 隐藏字段trx_id
    • 版本链
  • read view
  • 举例说明
    • read committed(读已提交)隔离级别下
    • repeatable read(可重复读)隔离级别下
    • innodb如何解决幻读
  • 总结

并发问题的解决办法:加锁或mvcc
读操作用mvcc,写操作加锁,读写不冲突,并发性好。mvcc的实现就是依赖于隐藏字段、undo log(多版本)、read view(控制)。
MySQL的存储引擎中,只有innodb支持mvcc。

快照读与当前读

不加锁的简单select读都是快照读,读的是历史数据。需要加锁的场景,读取的是记录的最新版本,加锁的select、对数据进行增删改都会进行当前读

隔离级别

事务有四个隔离级别,可能存在三种并发问题:
在这里插入图片描述
MySQL中默认的隔离级别为可重复读,它实际上解决了脏读、不可重复读和幻读,但并不是串行化。
在这里插入图片描述

隐藏字段,undo log 版本链

隐藏字段trx_id

trx_id :每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id 隐藏列。
roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
例如:
有一student表,id = 1,name = ‘张三’,class = ‘一班’
假设插入该记录的事务id为8,此条记录的结构如下:
在这里插入图片描述

版本链

每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer 属性( INSERT 操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来,串成一个链表,就是版本链。

在这里插入图片描述
对该记录每次更新后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer 属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。

read view

readview就是事务A在使用MVCC机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,innodb为每个事务构造了一个数组,用来记录并维护系统当前活跃(begin了但未commit)事务的id。

举例说明

mvcc只在read committed和repeatable read两个隔离级别下工作。

read committed(读已提交)隔离级别下

read committed:每次读取数据前都生成一个readview。
事务id的分配:如果是增删改行为,系统自动递增分配事务id;如果是查询行为,事务id为0.
现在有两个事务id 分别为10 、20 的事务在执行:

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...

student表中id为1的记录得到的版本链表如图:
在这里插入图片描述
此时有个事务隔离级别为read committed,进行如下操作:

begin;
select * from student where id = 1;

因为transaction 10 未提交,所以这个事务查出来的结果是“1 张三 一班 8”这条记录。接下来解释原因:

  1. 在执行select操作时,生成read view,针对当前操作的快照,快照中记录了信息:creator_trx_id = 0(查询操作事务id为0)、trx_ids列表内容[10,20](id为10和20的两个事务活跃)、up_limit_id = 10(小的事务是10)、low_limit_id = 21(最大的事务不会超过20);
  2. 从版本链中挑选可见的记录,最新的是“王五”的记录,trx_id = 10,与creator_trx_id = 0不等,而且10在trx_ids列表中,表明10这个事务还没有提交,肯定不能查出;
  3. 接着找下一个版本,“李四”的trx_id 还是 10,也不行;
  4. 再往下找到“张三”trx_id = 8,不在活跃的事务列表里,一定是之前已经提交的事务,那么就把“张三”这条事务读出来。

接下来,把transaction 10的事务提交,

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
COMMIT;

把transaction 20的事务中更新一下表student 中id 为1 的记录:

# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...
UPDATE student SET name="钱七" WHERE id=1;
UPDATE student SET name="宋八" WHERE id=1;

此时版本链变成了这样:
在这里插入图片描述
接下来,事务又读了一次:

begin;
# select 1: 10 20 未提交
select * from student where id = 1;		# 张三
# select 2: 10提交,20未提交
select * from student where id = 1;		# 王五
  1. **在read committed隔离级别下,每读一次,生成一个read view。**所以第二次select的时候,又生成了一个read view。该readview的trx_ids列表内容剩[20],up_limit_id = 20,low_limit_id = 21,creator_trx_id = 0;
  2. 从版本链中挑选,最新版本“宋八” trx_id = 20,还活跃着,就跳到下一个版本
  3. “钱七”的trx_id = 20,跳到下一版本
  4. "王五"的trx_id = 10,是提交过的,是生成read view之前的事务,所以查出这条记录。

repeatable read(可重复读)隔离级别下

只会在第一次查询时生成read view,之后的查询不会重复生成。
还是上面的例子:

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...

在这里插入图片描述
此时有个事务隔离级别为read committed,进行如下操作:

begin;
select * from student where id = 1;

同上,此时查到的是“张三”记录。read view中:creator_trx_id = 0(查询操作事务id为0)、trx_ids列表内容[10,20](id为10和20的两个事务活跃)、up_limit_id = 10(小的事务是10)、low_limit_id = 21(最大的事务不会超过20);

提交transaction 10,到transaction 20中更新表student,

# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...
UPDATE student SET name="钱七" WHERE id=1;
UPDATE student SET name="宋八" WHERE id=1;

在这里插入图片描述
在这个场景下,刚才读过的事务现在又读了一次:

begin;
# select 1: 10 20 未提交
select * from student where id = 1;		# 张三
# select 2: 10提交,20未提交
select * from student where id = 1;		# 张三

这时不再生成新的read view,还是一开始的read view。creator_trx_id = 0(查询操作事务id为0)、trx_ids列表内容[10,20](id为10和20的两个事务活跃)、up_limit_id = 10(小的事务是10)、low_limit_id = 21(最大的事务不会超过20);

  1. 版本链中最新的版本“宋八” trx_id = 20,是活跃的事务,跳过;
  2. “钱七” trx_id = 20,活跃的事务,跳过; (这两个是transaction 20中的更新,未提交)
  3. “王五” trx_id = 10,活跃的事务,跳过; (这是transaction 10 中的更新,已经提交,但此时的read view是首次select时生成的,所以认为transaction 10 还在活跃)
  4. “李四” trx_id = 10,活跃的事务,跳过;
  5. “张三” trx_id = 8,不在trx_ids列表中,查出。

所以说,只要查询事务没有提交,用的都是第一次select时生成的read view。

innodb如何解决幻读

幻读:student表中有一条id=1的记录,事务A开始进行第一次查询,结果显示id=1,现有事务B对student表进行更新操作,添加了id=2,id=3的两条记录并提交,接下来事务A再进行第二次查询,结果显示id=1,id=2,id=3,同一事务A前后两次查询操作的返回结果不相同,这就是出现了幻读。
在repeatable read隔离级别下,read view只在事务第一次进行查询操作时生成,此后的查询操作(只要不commit)仍用这个视图,所以就算事务B提交了更新,read view并不同步更新,trx_ids列表中仍有事务B的trx_id,认为事务B处于活跃状态,跳过更新的那条记录。

总结

MVCC = 隐藏字段 trx_id + undo log 版本链 + read view 快照

READ COMMITTD 在每一次进行普通SELECT操作前都会生成一个ReadView。
REPEATABLE READ 只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView。

说明:执行delete语句或更新主键的update语句并不会立即把对应的记录完全从页面中删除,而是执行一个所谓的delete mark操作,相当于只是对记录上打了一个删除标志位,这就是为MVCC服务的,因为可能需要回滚。

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

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

相关文章

LaTex使用技巧9:argmin / argmax下标写法

记录两种写法 1.arg⁡max⁡θ\mathop{\arg\max}\limits_{\theta}θargmax​的写法 写法1: $\mathop{\arg\max}\limits_{\theta}$ 写法2: $\sideset{}{}{\arg\max}_{\theta}^{} $ 2.arg⁡min⁡θ\mathop{\arg\min}\limits_{\theta}θargmin​的写法 写法…

STL常用生成算法和集合算法(20221207)

STL的常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric> 组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;涉及比较、交换、查找、遍历等等&#xff1b; <functional>定义了一些模板类&#xff0…

做一个公司网站大概要多少钱?

做一个公司网站大概要多少钱&#xff0c;很多公司在做网站之前可能已经简单了解过费用&#xff0c;但是费用差距都会比较大&#xff0c;为什么的呢&#xff0c;因为一般都是受到制作方式因素的影响。下面给大家说说不同的方式做一个公司网站大概要多少钱。 一、自己/团队做公司…

SQLyog —— 图形化工具使用

SQLyog下载链接&#xff1a; 点击跳转 在这一篇内容MySQL数据库 —— 常用语句当中讲到关于MySQL数据库命令的基本使用&#xff0c;这一篇是关于SQLyog数据库图形化工具的内容&#xff0c;先进行安装演示后在通过SQLyog进行操作数据库&#xff1a; SQLyog 安装 下载完成之后双击…

pageoffice在线打开word文件加盖电子印章

一、加盖印章的 js 方法 js方法 二、常见使用场景 1、常规盖章。弹出用户名、密码输入框&#xff0c;选择对应印章。 点击盖章按钮弹出用户名密码登录框&#xff0c;登录以后显示选择电子印章。 document.getElementById("PageOfficeCtrl1").ZoomSeal.AddSeal(…

Python模块pathlib操作文件和目录操作总结

前言 目前大家常用的对于文件和操作的操作使用 os.path 较多&#xff0c;比如 获取当前路径os.getcwd()&#xff0c;判断文件路径是否存在os.path.exists(folder) 等等。 在Python3.4开始&#xff0c;官方提供了 pathlib 面向对象的文件系统路径&#xff0c;核心的点在于 面向…

chatGPT代码写的有点好啊,程序员要失业了?

AI神器ChatGPT 火了。 能直接生成代码、会自动修复bug、在线问诊、模仿莎士比亚风格写作……各种话题都能hold住&#xff0c;它就是OpenAI刚刚推出的——ChatGPT。 有脑洞大开的网友甚至用它来设计游戏&#xff1a;先用ChatGPT生成游戏设定&#xff0c;再用Midjourney出图&…

element-plus elplus el-tree三种图标自定义 并且点击图标展开收起 点击文字获取数据

前言 公司需求,需要实现如下样式的树形列表 (基于vue3 element-plus) 当节点展开时,显示展开的文件夹图标,当节点收起时显示收起的文件夹,最后一级显示文件样式 废话没有了, 代码如下 <!-- 树形列表组件 --> <template><div class"tree-input" v-i…

Vue学习:回顾Object.defineProperty(给对象添加或者定义属性的)

<script>//定义对象let person{name:李四,sex:"男"}Object.defineProperty(person,age,{value:18});//参数:添加属性的对象 添加的属性名 配置项console.log(person)</script> 颜色不同&#xff1a;说明了age不可以枚举age属性不参与遍历 Object.keys(…

电脑屏幕录制怎么弄?电脑上怎么录制屏幕, 3个实用方法

对于日常办公的小伙伴来说&#xff0c;电脑、键盘、鼠标等办公设备都是不可分割的。事实上&#xff0c;不仅仅是在日常办公&#xff0c;在很多业余的活动中&#xff0c;也会使用到电脑设备。在使用电脑的时候&#xff0c;会经常有需要录制电脑屏幕的情况&#xff0c;比如记录会…

阿里云Linux热扩容云盘(growpart和resize2fs工具)

阿里云linux机器系统盘空间不够进行扩容 一、扩容物理盘 阿里云控制台在线扩容完成 二、安装growpart工具和resize2fs工具 [rootA ~]# yum install cloud-utils-growpart [rootA ~]# yum install xfsprogs 三、检查扩容磁盘属性 1、检查云盘大小 /dev/vda1显示容量为20G(在线…

Properties类的使用

Properties类是一个配置文件类&#xff0c;主要作用就是用来封装配置文件&#xff0c;将配置文件加载成为一个Properties对象。 注意&#xff1a;Properties类一般用来加载 .properties配置文件 首先看一下.properties配置文件的样子 driverClassNamecom.mysql.cj.jdbc.Drive…

电力系统潮流【牛顿-拉夫逊法】(4节点、5节点、6节点、9节点)(Matlab代码实现)

目录 1 概述 2 电力系统潮流计算概述 2.1 电力潮流发展进程 2.2牛顿拉夫逊法潮流计算 3 仿真结果 4 Matlab代码及文章讲解 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;⛳️⛳️⛳️ ​ 1 概述 最初&#xff0c;电力系统潮流计算是通过人工手算的。后…

Java内存区域与内存分配策略

java很聪明&#xff0c;它将手动改为自动&#xff0c;把内存的控制权交给了虚拟机&#xff0c;下面我们就来探究一下JVM是怎么进行自动内存管理的。 手动内存管理分为两部分&#xff1a;给对象分配内存和回收分配给对象的内存。 一、运行时数据区域 线程公有 在运行时数据区中…

基础入门 - SpringBoot 底层注解

目录 1、SpringBoot特点 1.1、依赖管理 1.2、自动配置 2、容器功能 2.1、组件添加 1、Configuration Spring Boot 在底层 Configuration 的两个配置 2、Import 3、Conditional 2.2、原生配置文件引入 1、ImportResource 2.3、配置绑定 1、ConfigurationProperties …

前端开发踩坑笔记(2022-11)

文章目录1、Mac上SourceTree更新已删除的远端分支和tag2、echarts x轴文字显示不全&#xff08;解决方案&#xff09;3、如何渲染多行多列的表格&#xff08;非固定的行数和列数&#xff09;4、umy-ui标题过长或内容过长时的处理5、dateRange的时间选择只能选择一个周6、如何将…

Servlet API 详解

目录 一、HttpServlet ① init() 方法 ② service() 方法 ③ destroy() 方法 ④ doGet()方法 ⑤ doPost()方法 ⑥ doPut/deDelete/doOptions 常见面试题&#xff1a; 请你谈谈Servlet的生命周期 二、Http请求&#xff1a;HttpServletRequest 1. 获取请求行信息 2. 获…

一个redux使用案例模板

目录 redux 纯函数和高阶函数&#xff1a; redux 开发工具使用 react-redux redux 1. 结构&#xff1a; count--index.jsx import React, { Component } from react import store from ../../redux/store import { acDecrement,acIncrement,acAsyncIncrement } from ../..…

不会向上管理的人,做不好项目经理和PMO【附具体行动清单】

在职场中&#xff0c;向上管理基本是最重要的一件事儿&#xff0c;升职涨薪奖金都离不开向上管理&#xff01;当你的向上管理做得好&#xff0c;机会都会迎面扑来。 你是不是也遇到过被领导批评时&#xff0c;感到非常委屈或愤怒&#xff0c;情绪经常被领导左右&#xff0c;那…

Three.js一学就会系列:02 画线

系列文章目录 Three.js一学就会系列&#xff1a;01 第一个3D网站 文章目录系列文章目录[Three.js一学就会系列&#xff1a;01 第一个3D网站](https://blog.csdn.net/u012551928/article/details/128205373)前言一、省略部分二、使用方法创建一个场景创建一个透视摄像机将渲染器…