事务隔离级别的无锁实现方式 -- MVCC

MVCC的全称是Multiversion Concurrency Control(多版本并发控制器),是一种事务隔离级别的无锁的实现方式,用于提高事务的并发性能,即事务隔离级别的一种底层实现方式。

在了解MVCC之前,我们先来回顾一些简单的知识点:数据库的隔离级别和并发场景。

数据库的隔离级别

主要用来解决多个事务在并发的情况下对同一个数据进行读写操作所产生的一些列线程不安全的问题。

  • 脏读 – 读未提交(RU)
    • 事务A读取到了事务B还未提交的数据。
  • 不可重复读 – 读已提交(RC)
    • 事务A读取到事务B已经提交的数据,可以解决脏读问题,但会出现不可重复读。
  • 可重复读(RR)
    • 同一是事务下,事务在执行期间,多次读取同一数据时,能够保证读取到的数据是一致的,可以解决不可重复读问题,但在事务读取过程中,如果有另外一个新事务新增/变更,会出现幻读。
  • 串行化(serializable)
    • 最高的隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读,幻读和不可重复读的问题,但是这种事务隔离级别效率最低,比较耗费数据库性能。

读已提交:

解决脏读问题为什么不使用行锁?如果我们在事务一中对数据进行修改操作时给数据添加一个行锁,那么接下来的事务中想要执行SQL语句进行查询操作,那么该操作将会被阻塞,直到修改操作执行完毕,行锁被解开为止,如此一来,就会降低并发性。

三种并发场景

  • 读 - 读
    • 不存在线程安全问题,不需要关心并发
  • 读 - 写
    • 有线程安全问题,可能会存在数据更新丢失的问题,比如第一类更新丢失,第二类更新丢失

第一类更新丢失:事务A回滚时,将已经提交的事务B更新的数据覆盖了

第二类更新丢失:事务A提交覆盖了事务B已经提交的数据,造成事务B所做的操作丢失

什么是MVCC

定义

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

目的

在最早的数据库系统中只有读 - 读之间可以并发,读 - 写、写 - 写之间都要阻塞。在引入多版本并发控制技术之后,只有写 - 写之间相互阻塞,其他三种操作都可以并行,这样就达到了提高InnoDB引擎并发度的目的。

实现

在MySQL的内部实现中,InnoDB是通过undo log实现,undo log可以找回数据的历史版本。找回的历史版本可以提供给用户读(按照隔离级别定义,有些读请求只能看到比较老的数据版本),也可以在回滚的时候覆盖数据页上的数据,在InnoDB内部中,会记录一个全局的活跃读写事务数据组,其主要用来判断事务的可见行

MVCC的三个关键点

MVCC是如果无锁地实现事务的隔离级别的呢?主要就是靠以下三个关键因素:

隐藏列

在数据库表单中除了我们创建的原数据的列外,数据库帮我们维护的三个看不到的隐藏列:DB_TRX_ID(事务ID),DB_TRX_ID(存储旧的事务的指针),(ROW_ID)
在这里插入图片描述

undo log

回滚实现:

通过两个隐藏列trx_id(最近一次提交事务的ID)和roll_pointer(上个版本的地址)建立一个版本链。并在事务中读取的时候生成一个Read View(读视图),在Read Committed隔离级别下,每次读取都会生成一个读视图,而在Repeatable Read隔离级别下,只会在第一次读取时生成一个读视图

在这里插入图片描述

ReadView

定义

不加锁的select就是快照读,即不加锁的非阻塞读。(快照读的前提是非serializable隔离级别,在该隔离级别下,快照读会退化为当前读),之所以出现快照读,是基于高并发性能的考虑,快照读的实现是基于MVCC的,可以认为MVCC是行锁的变种,但是他在很多情况下避免了加锁操作,降低了开销。因为多版本的原因,导致快照读可能读取到的不一定是数据的最新版本,而有可能是历史版本

当前读和快照读与MVCC关系

MVCC可以理解为是一个“维护数据的多个版本,使得读写操作没有冲突”的概念,是一种理想状态。而在MySQL中,快照读,就是实现MVCC理想模型的其中一个具体的非阻塞读功能,而相对而言,当前读就是一个悲观锁的具体功能实现

具体实现&&判断顺序

存储三条数据:creator_trx_id(当前事务ID),min_trx_id(当前未提交的事务的ID),max_trx_id(未开始的事务)

  • 首先通过undolog拿到最新版本的数据,最新一次修改本条数据的事务ID
  • 第一次判断:将当前事务ID与最新一次修改本条数据的事务ID进行比较
    • 二者相等–>本条版本的数据是在当前事务中保存的–>该条数据可以读取
    • 二者不相等则不能直接拿到该值,继续进行判断
  • 第二次判断:比较最新一次修改本条数据的事务ID和最小的事务ID
    • 小于–>当前版本数据是在事务开始之前保存的–>该条数据可以读取到
    • 大于等于–>不能直接拿到该值,继续进行判断
  • 第三次判断:比较最新一次修改本条数据的事务ID和最大的事务ID
    • 大于–>当前版本的数据是在本事务开始之后保存的–>本数据不能被读取–>顺着指针找到上一个版本的数据
  • 第四次判断:比较最新一次修改本条数据的事务ID是否在最小事务ID和最大事务ID之间
    • 存在于二者之间–>当前版本的数据是在未提交的并发事务中
      • 如果事务隔离级别是读已提交–>该条数据不能被读取
    • 不存在于二者之间–>沿着指针找到上一个版本的数据,再进行以上四次判断

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

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

相关文章

最优算法100例之47-从尾到头打印单链表

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 从尾到头打印单链表 题解报告 方法1:头插法逆置单链表然后依次打印;注意此处是不带头结点的单链表,带头节点的操作稍微有…

网络篇09 | 运输层 udp

网络篇09 | 运输层 udp 01 简介UDP 是面向报文的 02 报文协议 01 简介 UDP 只在 IP 的数据报服务之上增加了一些功能:复用和分用、差错检测 UDP 的主要特点:无连接。发送数据之前不需要建立连接。 使用尽最大努力交付。即不保证可靠交付。 面向报文。…

【信号处理】心电信号传统R波检测定位典型方法实现(matlab)

关于 心电信号中QRS波检测是一个非常重要的步骤,可以用于实现重要波群的基本定位,在定位基础上,可以进一步分析心电信号的特征变化,从而为医疗诊断提供必要的参考。 工具 MATLAB ECG心电信号 方法实现 ECG心电信号加载 ecg …

基于深度学习的花卉检测系统(含PyQt界面)

基于深度学习的花卉检测系统(含PyQt界面) 前言一、数据集1.1 数据集介绍1.2 数据预处理 二、模型搭建三、训练与测试3.1 模型训练3.2 模型测试 四、PyQt界面实现参考资料 前言 本项目是基于swin_transformer深度学习网络模型的花卉检测系统,…

架构师系列-搜索引擎ElasticSearch(八)- 集群管理故障恢复

故障转移 集群的master节点会监控集群中的节点状态,如果发现有节点宕机,会立即将宕机节点的分片数据迁移到其它节点,确保数据安全,这个叫做故障转移。 下图中node1是主节点,其他两个节点是从节点 节点故障 此时node1…

【uniapp】vscode安装插件、ts校验、允许json文件注释

1、vscode安装的插件: uni-create-viewuni-hlperuniapp小程序扩展 2、ts校验 安装插件: pnpm i -D types/wechat-miniprogram uni-helper/uni-app-types配置tsconfig.json {"extends": "vue/tsconfig/tsconfig.json","compi…

论文笔记:The Expressive Power of Transformers with Chain of Thought

ICLR 2024 reviewer 评分 6888【但是chair 很不喜欢】 1 intro 之前的研究表明,即使是具有理想参数的标准Transformer,也无法完美解决许多大规模的顺序推理问题,如模拟有限状态机、判断图中的节点是否相连,或解决矩阵等式问题 这…

IOS 短信拦截插件

在使⽤iOS设备的时候, 我们经常会收到1069、1065开头的垃圾短信, 如果开了iMessage会更严重, 各种乱七⼋糟的垃圾信息会时不时地收到。 从iOS11开始, ⼿机可以⽀持恶短信拦截插件了. 我们可以通过该插件添加⼀些规则通过滤这些不需要的信息. ⼀. 使⽤xcode新建⼀个项⽬ 【1】…

MongoDB 初识

介绍 MongoDB是一种开源的文档型数据库管理系统,它使用类似于JSON的BSON格式(Binary JSON)来存储数据。与传统关系型数据库不同,MongoDB不使用表和行的结构,而是采用集合(Collection)(Mysql表)和…

LabVIEW直流稳定电源自动化校准系统

LabVIEW直流稳定电源自动化校准系统 直流稳定电源正向着智能化、高精度、多通道、宽量程的方向发展。基于LabVIEW开发环境,设计并实现了一种直流稳定电源自动化校准系统,以提升校准过程的整体效能,实现自动化设备替代人工进行电源校准工作。…

【DNS】

文章目录 DNS域名解析系统(Domain Name System)DNS系统需要解决的问题DNS域名解析系统(Domain Name System)问题1:DNS名字空间(The DNS Name Space)DNS名字空间(The DNS Name Space)DNS名字空间(The DNS Na…

oracle 19c 主备 补丁升级19.22

补丁升级流程 备库升级 备库备份$ORALCE_HOME du -sh $ORACLE_HOME ​​​​​​​ 备份目录将dbhome_1压缩 cd $ORACLE_HOME cd .. Ls tar -cvzf db_home.tar.gz db_home_1 /opt/oracle/product/19c ​​​​​​​​​​​​​​ 关闭监听关闭数据库查看sq…

基于表面势的增强型p-GaN HEMT器件模型

来源:电子学报 22年 摘要 为了满足功率电路及系统设计对p-GaN HEMT(High Electron Mobility Transistor)器件模型的需求,本文建立了一套基于表面势计算方法的增强型p-GaN HEMT器件SPICE(Simulation Program with Int…

【opencv】示例-travelsalesman.cpp 使用模拟退火算法求解旅行商问题

// 载入 OpenCV 的核心头文件 #include <opencv2/core.hpp> // 载入 OpenCV 的图像处理头文件 #include <opencv2/imgproc.hpp> // 载入 OpenCV 的高层GUI(图形用户界面)头文件 #include <opencv2/highgui.hpp> // 载入 OpenCV 的机器学习模块头文件 #includ…

数据库:SQL分类之DQL详解

1.DQL语法 select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数 基本查询 条件查询&#xff08;where&#xff09; 聚合函数&#xff08;count、max、min、avg、sum &#xff09; 分组查询&…

Linux网络基础 (二) ——(IP、MAC、端口号、TCPUDP协议、网络字节序)

文章目录 IP 地址基本概念源IP地址 & 目的IP地址 MAC 地址基本概念源MAC地址 & 目的MAC地址 端口号基本概念源端口号 & 目的端口号 TCP & UDP 协议基本概念TCP 与 UDP 的抉择 网络字节序大端、小端字节序 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alask…

OLTP 与 OLAP 系统说明对比和大数据经典架构 Lambda 和 Kappa 说明对比——解读大数据架构(五)

文章目录 前言OLTP 和 OLAPSMP 和 MPPlambda 架构Kappa 架构 前言 本文我们将研究不同类型的大数据架构设计&#xff0c;将讨论 OLTP 和 OLAP 的系统设计&#xff0c;以及有效处理数据的策略包括 SMP 和 MPP 等概念。然后我们将了解经典的 Lambda 架构和 Kappa 架构。 OLTP …

杰发科技AC7840——CAN通信简介(5)_可变波特率设置

0. 简介 设置可变波特率时候&#xff0c;遇到2个坑&#xff0c;在此记录下来 使用该函数即可 can_time_segment_t bitrate2 s_canBitrate[CAN_BITRATE_250K]; CAN_DRV_SetBitrate(instance, &bitrate2); 1. 波特率指针注意不要空 查看设置波特率的接口&#xff0c;发现…

Pytest精通指南(09)利用Fixture给函数设置别名

文章目录 前言测试用例默认显示传递一个参数传递多个参数 利用Fixture修改测试函数名称传递一个参数传递多个参数 验证ids和params长度不一致修改Fixture函数名称 前言 在 pytest 中&#xff0c;pytest.fixture 装饰器用于定义可以在多个测试函数中重用的设置和清理代码。 name…

C/C++基础----内存相关

malloc分配内存 用法 参数为要开辟内存的大小&#xff08;字节为单位&#xff09;返回值为void*,所以要强转一下语法&#xff1a;malloc()动态开辟20个字节的内存&#xff0c;代码&#xff1a;#include <iostream>using namespace std;int main() {int *a (int *) mal…