MVCC相关

news2024/11/25 6:36:57

文章目录

  • 前情要点
    • 基于什么引擎
    • 并发事务产生的问题
    • 不可重复读和幻读区别
    • Next-Key Lock的示例
    • 解决并发事务采用的隔离级别
    • 当前读(Current Read)
    • 快照读(Snapshot Read)
    • 参考
  • MVCC
    • 定义
    • 表里面的隐藏字段
    • 由db_roll_ptr串成的版本链
    • ReadView
    • 可见性算法
    • mvcc的可见性算法为什么要以提交的数据为准则?
    • RC和RR使用MVCC的不同
    • RR级别下是否完全解决幻读问题?
  • 不同隔离级别下的实现原理
  • 特别鸣谢


前情要点

基于什么引擎

mvcc和事务在mysql中都是基于InnoDB引擎下,抛开这个引擎下谈论就没有意义了,因为并不是所有的引擎都支持事务的,例如MyISAM就不支持。

常见的一些引擎:

在这里插入图片描述

并发事务产生的问题

在明白什么是mvcc之前,需要先知道在mysql中两个读的概念;

而在了解什么是当前读和快照读之前,需要先明白在并发事务中常见的问题,如下:

并发事务产生的问题:
1.脏读:

A事务对C数据修改,未提交,B事务读取到这个C数据,并使用。这个修改后的C数据是未提交的,属于脏数据。

2.丢失修改:

A事务对C数据修改,修改为C-1;这个时候B事务也对C数据修改,修改成C-1,最终数据修改为C-1,但是实际上正确修改的数据应该为C-1-1。也就是说A事务的修改被丢失了。

3.不可重复读:

A事务第一次读取C数据10之后,B事务修改C数据为20;这个时候A事务第二次读取C数据为20。不可重复读侧重于更新(update),解决不可重复读是加行级锁(所以更多针对的是表中一行数据)。

4.幻读:

A事务第一次读取C数据总数据为10之后,B事务添加到C数据2条新的数据;这个时候A事务第二次读取C数据总数据为12。幻读主要在于添加(insert)和删除(delete),解决幻读是加间隙锁+Next-Key-Lock(所以更多针对的是(范围内的数据集)整张表的数据

第二点丢失更新属于写写问题,本文不涉及。
从这些读取问题中,可以看到如果在使用读取语句的时候不采取任何措施,那么就意味着我们每次读取到数据并不一定是最新,也不一定是已提交的数据。

不可重复读和幻读区别

在网上能够搜到很多文章,都没准确的说明这两个的区别,对于这两个概念一定要理解透,因为对后续理解mvcc很重要。

不可重复读和幻读的区别:
不可重复读:多次读取同一行数据,读到的值可能不同(其他事务对共享资源的更新)。
解决不可重复读的主要方法是使用 Repeatable Read 隔离级别。主要涉及行级锁,防止同一行数据在同一事务中多次读取时出现不一致

幻读:多次执行相同的范围查询,结果集中的行数可能不同(其他事务对共享资源的增加和删除)。
解决大部分幻读问题方法涉及间隙锁(Gap Lock)和 Next-Key Lock,防止在范围查询中出现新的行或者被删除了行。完全解决幻读的主要方法是使用 Serializable 隔离级别。

以下来自于chatgpt4.0:
在这里插入图片描述

PS: Next-Key Lock:行级锁和间隙锁的组合。它锁定行记录及其前后的间隙,防止在范围查询中出现新的插入或删除操作,从而避免幻读

Next-Key Lock的示例

Next-key Lock的示例如下来自于chatgpt4.0):
在这里插入图片描述

以下是关于间隙锁和Next-Key Lock锁的异同(来自于chatgpt4.0):
在这里插入图片描述

解决并发事务采用的隔离级别

事务隔离级别:

1.读未提交(READ UNCOMMITTED):允许读取尚未提交的数据,可能导致脏读,幻读,不可重复读,丢失更新。

2.读已提交(READ COMMITTED):读取已经提交的数据,阻止了脏读发生,可能导致幻读、不可重复读,丢失更新。

3.可重复读(REPEATABLE READ):解决了脏读,不可重复读的问题。仍然可能发生幻读,丢失更新。

4.串行化(SERIALIZABLE): 解决了脏读,不可重复读,幻读问题,丢失更新。最高等级,最低效率。。。。事务按照顺序执行。

更详细的事务隔离级别可看这个文章:
结合图文一起搞懂MySQL事务、MVCC、ReadView!

当前读(Current Read)

当前读就是读的是当前时刻已提交的数据。

在mysql中,当前读返回的记录都会加上锁,保证其他事务不会再并发的修改这条记录,也就是说在并发事务中读取到的是已经提交的最新的数据。

例如:

select lock in share mode (共享锁),
select for update; update; insert;
delete (排他锁)

以上操作都是属于当前读。在串行化的事务隔离级别下因为事务都加锁,所以读取到的都属于当前读。

快照读(Snapshot Read)

跟当前读相对应的就是快照读,快照读读取到的不是最新的数据,读取到往往是某个快照版本,而不是当前最新的数据。

在 mysql中,快照读不使用锁,而是通过 MVCC 实现。

例如:

– 快照读示例:普通的 SELECT 语句使用快照读
SELECT column1, column2 FROM table_name WHERE condition;

就是在mysql的默认引擎(InnoDB)的默认隔离级别可重复读(Repeatable Read )下,使用select语句不加锁都是快照读。

参考

【MySQL】MVCC详解与MVCC实现原理(MySQL专栏启动)

当前读与快照读

MVCC

定义

MVCC(Multi-Version Concurrency Control):多版本并发控制。常用于数据库管理系统里面无锁的实现并发控制的方法,同时也是事务隔离级别的一种实现方式,提高了事务的并发性能。(Mysql的innoDB存储引擎使用了。Oracle的undo表空间实现MVCC,PostgreSQL也使用了)

在mysql中读已提交(RC,Read Committed)和可重复读(RR,Repeatable Read)都是通过MVCC实现的。

在了解定义之后,要理解MVCC的实现原理需要需要先理解几个概念。

表里面的隐藏字段

在mysql表里面的行数据中,有三个隐藏的列字段,分别是:db_row_id(唯一行号),db_trx_id(事务id),db_roll_ptr(回滚指针)。

此处主要在于下面两个字段:

db_trx_id:
每次⼀个事务对某条聚簇索引记录进⾏改动时,都会把该事务的事务id赋值给trx_id隐藏列

db_roll_ptr:
在undo log日志中存储的每行db_trx_id的上一个版本的回滚指针。每次对某条聚簇索引记录进⾏改动时,都会把旧的版本写⼊到undo⽇志中,然后这个隐藏列就相当于⼀个指针,可以通过它来找到该记录修改前的信息。

ps:
聚簇索引:索引和实际存储的数据在同一个地方,找到索引即找到实际数据值
非聚簇索引::索引和实际存储的数据不在同一个地方,找到索引还需要根据其他索引才能找到实际数据值

以下图片来自于全网最详细MVCC讲解,一篇看懂:
在这里插入图片描述

由db_roll_ptr串成的版本链

上面隐藏字段提到的Undo Log,Undo Log会包含每条更新记录的版本信息,包括旧版本的列数据和db_trx_id(事务id)以及db_roll_ptr(指向上一个版本的指针),这样就组成了一个版本链。

下图来自于:MVCC原理 - 我隔壁是老王 - 博客园
在这里插入图片描述

不同事务或者相同事务对同一记录行的修改,会使该记录行的 undo log 成为一条链表,链首就是最新的记录,链尾就是最早的旧记录

关于UndoLog日志更为详细的内容,请参考:
【MySql进阶】undo日志详解:undo日志结构、undo日志链表、回滚段、undo log原理

ReadView

一致性视图,全称 Read View ,是用来判断版本链中的哪个版本对当前事务是可见的条件,也就是说判断版本链中哪个版本对当前事务是可以读取到的,包含m_ids,m_creator_trx_id,m_low_limit_id,m_up_limit_id。

下图来自于:全网最详细MVCC讲解,一篇看懂 - 知乎
在这里插入图片描述

可见性算法

可见性指的是,当执行一个查询语句(快照读)的时候,当前事务的查询语句可以见到版本链哪条记录。

由隐藏字段db_trx_id(事务id),db_roll_ptr(回滚指针)和Undo Log版本链,Readview就可以实现MVCC了,其对比过程如下:

在这里插入图片描述

1.如果被访问版本的 DB_TRX_ID 属性值小于 Read View 中的 m_up_limit_id 值,说明生成该版本的事务在当前事务生成 Read View 之前已经提交,因此该版本可以被当前事务访问。
2.如果被访问版本的 DB_TRX_ID 属性值位于 Read View 的 m_up_limit_id 和 m_low_limit_id 之间(包括边界),则需要进一步检查 DB_TRX_ID 是否在m_ids 列表中。如果在列表中,说明在创建ReadView时生成该版本的事务仍处于活跃状态,因此该版本不能被访问;如果不在列表中,说明在创建 Read View 时生成该版本的事务已经提交,因此该版本可以被访问。
3.如果被访问版本的 DB_TRX_ID 属性值大于或等于 Read View 中的 m_low_limit_id 值,说明生成该版本的事务在当前事务生成 Read View 之后才提交,因此该版本不能被当前事务访问。
4.如果被访问版本的 DB_TRX_ID 属性值与 Read View 中的 m_creator_trx_id 值相同,表示当前事务正在访问自己所修改的记录,因此该版本可以被当前事务访问。

mvcc的可见性算法为什么要以提交的数据为准则?

从上面可见性算法的对比过程可以看到,其宗旨都是以已经提交了的数据为其是否可见的判断依据的,那么为什么要以这种已经提交的数据为准?

因为mvcc实现的是在可重复读的隔离级别下保证并发事务的有效进行的方式;在可重复读 的隔离级别下,目的就是为了防止脏读即读未提交,不可重复读的并发问题,所以其准则肯定要是读已提交的事务为准。

RC和RR使用MVCC的不同

不同的地方在于可重复读是在第一次查询的时候(快照读)生成的readview,该readview一直被使用,不会生成新的readview;而读已提交则是每次查询的时候(快照读)都生成新的readview从而实现了读已提交。

至于详细的关于其不同的示例,请参考文章中的RC 和 RR 下的 Read View章节:
全网最详细MVCC讲解,一篇看懂

RR级别下是否完全解决幻读问题?

全网最详细MVCC讲解,一篇看懂 - 知乎我看的这篇文章中,是说防止了部分幻读问题,但是没有完全解决幻读问题。这个结论是对的,但是它的示例却是错误的。。。。

在该文章中关于RR 级别下能否防止幻读两个示例如下:
示例1为:
在这里插入图片描述


事务B第一次正常读取(select),而事务B第二次读取使用加锁读取(for update)

示例2为:
在这里插入图片描述


事务B第一次正常读取(select),而事务B第二次读取前使用update语句之后再读取;

这两个示例在我第一次读懂之后,也觉得幻读仍然存在,因为按照第一次查询快照读,第二次查询当前读的机制来看,那么也就意味着即使未提交的事务也可以被当前读读取到。因而读取到最新的数据会导致幻读问题。然而,我还是有点疑心,所以就询问了gpt4.0一样的场景,gpt给出的回答是这两个场景仍然不会有幻读问题。问了好几次,才明白过来它为什么这么说。

需要注意的是,上面所展示的事务示例都是在RR的隔离级别下进行示例;

在事务B的第二次读取时,采用加锁查询也好,update之后再读取也罢;都是在两次读取之间采用了当前读和快照读的不同读取方式,而使用第二次读取采用当前读的方式获取最新的数据看起来会致使读取到不同的数据导致幻读,但是忽略了一个点:未提交的事务对于当前事务来说是不可见的。

无论是加锁也好还是MVCC都是为了实现读取到已经提交的事务,来避免出现并发事务的读取问题。

在上面两个示例里面,事务A的插入对于事务B第二次的加锁读取都不是可见的,也就是说事务B压根不知道事务A的插入,因为当前读在mysql中采用的是加锁的方式去获取最新的已提交数据,试问加锁的情况下怎么可能读取到未提交的事务呢?

那么在RR级别下什么时候会出现幻读问题?

在自增列或无索引列的情况下可能导致幻读问题。(来自于chatgpt4.0)

这个结论是可以闭环的。还记得前面说到不可重复读和幻读的区别的时候提到的Next-Key Lock和间隙锁吗?这两种锁是实现幻读的基础,如果锁出现问题了那么也就意味着幻读问题可能会发生。

这两种锁都是基于索引,其原因(基于chatgpt4.0)如下:
在这里插入图片描述


上图原因总结如下:



1.在无索引的情况下,MySQL 无法有效地使用间隙锁和 Next-Key Lock 来精确锁定范围。只能锁定大范围,不能完全锁住,可能导致幻读问题。



2.自增列的自动递增特性使得未来的插入无法被预锁定。无法预见未来的自增范围,导致新插入的记录不再锁定范围内,只能锁定现有记录和其后的间隙,可能导致幻读问题。



所以可以得出结论如下:
RR级别下没有完全解决幻读问题,当无索引或自增列的情况下会导致间隙锁和Next-Key Lock锁失效的情况,因而会导致幻读问题再发生。

不同隔离级别下的实现原理


不同隔离级别下的锁机制如下:


在这里插入图片描述




总结如下:
1.读未提交:不使用锁,无须其他手段;
2.读已提交:使用行级锁;
3.可重复读:MVCC+ 行级锁 + 间隙锁 + Next-Key Lock
3.可重复读:表级锁

特别鸣谢

结合图文一起搞懂MySQL事务、MVCC、ReadView!

全网最详细MVCC讲解,一篇看懂 - 知乎

MVCC原理

MVCC底层实现原理

Chatgpt4.0

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

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

相关文章

【哈希映射 字符串 乘法原理】2227. 加密解密字符串

本文涉及知识点 哈希映射 字符串 乘法原理 LeetCode 2227. 加密解密字符串 给你一个字符数组 keys ,由若干 互不相同 的字符组成。还有一个字符串数组 values ,内含若干长度为 2 的字符串。另给你一个字符串数组 dictionary ,包含解密后所…

2024年收集搜索引擎蜘蛛大全以及浏览器模拟蜘蛛方法

对于做SEOer来说经常和搜索引擎蜘蛛打交道,下面整理收集了最全的搜索引擎蜘蛛大全。供有需要的朋友使用,建议收藏。 搜索引擎蜘蛛大全 "TencentTraveler", "Baiduspider", "BaiduGame", "bingbot",//必应蜘蛛…

深入浅出递归算法

文章目录 递归思想递归的题目1.汉诺塔问题问题分析代码展示 2.合并两个有序链表问题分析代码展示 3.反转链表问题分析代码展示 4.两两交换 链表中的节点问题分析代码展示 总结 递归思想 递归就是将一个很大的问题拆分成子问题,然后再将子问题继续拆分,拆…

安全设计 | Microsoft 威胁建模工具Threat Modeling Tool安装及使用详解(文末附样例)

1. 概览 微软威胁建模工具(Threat Modeling Tool)是 Microsoft 安全开发生命周期 (SDL,Security Develop LifeCycle) 的核心要素。 当潜在安全问题处于无需花费过多成本即可相对容易解决的阶段,软件架构师可以使用威胁建模工具提…

C语言 | Leetcode C语言题解之第109题有序链表转换二叉搜索树

题目: 题解: int getLength(struct ListNode* head) {int ret 0;while (head ! NULL) {ret, head head->next;}return ret; }struct TreeNode* buildTree(struct ListNode** head, int left, int right) {if (left > right) {return NULL;}int …

力扣刷题---409. 最长回文串【简单】

题目描述 给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。 在构造过程中,请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。 示例 1: 输入:s “abccccdd” 输出:7 解释: 我们可以构造的最长的回文串…

分享:大数据风险检测报告,哪里查询比较好?

随着大数据技术的发展,逐渐被运用到各个领域,基于大数据技术的个人风险检测也就是我们常说的大数据报告在金融环境中运用的十分普遍,那大数据风险检测报告哪里查询比较好呢?本文就为大家简单介绍一下。 大数据风险检测报告查询能查到什么? …

Leetcode刷题笔记3

18. 四数之和 18. 四数之和 - 力扣(LeetCode) 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应&…

骨位深间距小模具镶件如何走水路?3D打印让一切简单

在模具制造领域,骨位深且间距小的模具镶件由于结构复杂,传统加工方法难以制造出符合要求的冷却水路,导致模具在注塑过程中容易产生热量积聚,进而引发烫伤、缩孔等不良。然而,随着3D打印技术的飞速发展,这些…

爬虫逆向实例小记——某数据知识管理网站-DES-ECB模式

aHR0cHM6Ly9rZC5uc2ZjLmNuL2ZpbmFsUHJvamVjdEluaXQ 注意:本文是逆向部分比较少,主要为了流程走通,限于代码搬运工。 第一步:分析页面 此网站经过请求响应,可以看出响应内容为加密内容。 第二步:判断加密类型 在XHR …

【Linux】从零开始认识进程间通信 —— 管道

送给大家一句话: 人要成长,必有原因,背后的努力与积累一定数倍于普通人。所以,关键还在于自己。 – 杨绛 从零开始认识进程间通信 1 为什么要进程间通信2 进程如何通信3 进程通信的常见方式4 管道4.1 什么是管道4.2 管道通信的系…

postgresql insert on conflict 不存在则插入,存在则更新

向一张表执行插入动作,如果插入的字段数据已存在,则执行更新操作,不存在则进行插入操作。 1、创建一张表 CREATE TABLE "user_info" ( "id" int2 NOT NULL, "name" varchar(20) COLLATE "pg_catalog&quo…

近邻算法详细

近邻算法,特别是K-近邻算法(K-Nearest Neighbors, KNN),是一种基于实例的学习方法,广泛应用于分类和回归分析任务。下面是K-近邻算法的详细说明: 基本概念 K-近邻算法的核心思想是“物以类聚”&#xff0…

基于transformers框架实践Bert系列1--分类器(情感分类)

本系列用于Bert模型实践实际场景,分别包括分类器、命名实体识别、选择题、文本摘要等等。(关于Bert的结构和详细这里就不做讲解,但了解Bert的基本结构是做实践的基础,因此看本系列之前,最好了解一下transformers和Bert…

node版本管理nvm详细教程

安装 nvm 之前先清理node相关的所有配置,如环境变量、.npmrc文件、node_cache、node_global 等 一、下载nvm 任选一处下载即可 官网:Releases coreybutler/nvm-windows (github.com) 码云:nvm下载仓库: nvm下载仓库 百度网盘&#xff1…

基于GA遗传优化的CNN-GRU的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 CNN-GRU模型架构 4.2 GA优化CNN-GRU流程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ...........................................…

雷电预警监控系统:守护安全的重要防线

TH-LD1在自然界中,雷电是一种常见而强大的自然现象。它既有震撼人心的壮观景象,又潜藏着巨大的安全风险。为了有效应对雷电带来的威胁,雷电预警监控系统应运而生,成为现代社会中不可或缺的安全防护工具。 雷电预警监控系统的基本…

代码随想录算法训练营第14天 |● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代

文章目录 前言二叉树的递归遍历💖递归算法基本要素代码 迭代遍历-需要先理清思路再写前向迭代法后序迭代中序迭代 迭代法统一写法总结 前言 理论基础 需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义 记录我容易忘记的点 题目…

打造AI虚拟伴侣 - 优化方案

第一部分:框架优化概述 1、精确定位: 构建一个高度灵活且用户友好的平台,旨在通过无缝集成多种大型语言模型(LLMs)后端,为用户创造沉浸式的角色交互体验。不仅适配电脑端,还特别优化移动端体验,满足二次元AI虚拟伴侣市场的特定需求。 2、核心功能强化: 增强后端兼容…

大数据Hive中的UDF:自定义数据处理的利器(下)

在上一篇文章中,我们对第一种用户定义函数(UDF)进行了基础介绍。接下来,本文将带您深入了解剩余的两种UDF函数类型。 文章目录 1. UDAF1.1 简单UDAF1.2 通用UDAF 2. UDTF3. 总结 1. UDAF 1.1 简单UDAF 第一种方式是 Simple(简单…