美团面试官:可重复读隔离级别实现原理是什么?(一文搞懂MVCC机制)

news2024/11/14 20:33:44

本文首发于公众号【看点代码再上班】,欢迎围观,第一时间获取最新文章。

原文:美团面试官:可重复读隔离级别实现原理是什么?(一文搞懂MVCC机制)

“全文共计4270字,预计阅读时间6分钟

大家好,我是tin,这是我的第26篇原创文章

还记得MySQL数据库事务都有哪些隔离级别么?如果忘记可以到这里重新温习:还傻傻搞不懂MySQL事务隔离级别么(图文并茂,保证你懂!)。

今天主要说一说MySQL是如何实现可重复读隔离级别的, 话不多说,先上一个目录:

一、MVCC的实现原理

    1.1 什么是MVCC

    1.2 两个隐式列

    1.3 undo日志 

    1.4 Read View读视图

二、可重复读实现原理

    2.1 什么是可重复读

    2.2 基于多版本实现可重复读

    2.3 可重复读可以解决幻读么

三、结语

一、MVCC的实现原理

在讲可重复读隔离级别之前,必须先来了解MVCC的实现原理,因为它的实现依赖MVCC,而MVCC的实现又依赖undo log和 Read View,下面一一讲解。

1.1 什么是MVCC

MVCC,直译多版本并发控制,它的全称是Multi-Version Concurrency Control, 直白说就是在同一时刻同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制。

在MySQL InnoDB中,MVCC的实现主要是为了提高数据库并发性能,它能很好地处理MySQL的读写冲突,做到尽量不加锁。

比如现在我的账户表中有这样一条Tom的余额记录:

除了正常id,user_name,balance字段以外,其实还有两个隐藏字段,我已一并画出,后续会补充讲解。

如果同时有多个事务操作这行数据时,为了避免多事务并发场景下数据不一致问题的出现,InnoDB会通过一条 "链条"把每个事务的数据"快照版本"串联起来, 比 如下面这样:

每个事务启动的时候都会给自己分配一个事务ID(trx_id)并生成一个当时所有活跃事务的事务ID列表(其实就是Read View,下文有讲到)。

当一个事务操作更新一条记录后,会同时把当前事务ID更新到这条数据记录上(隐藏字段)。当下一个事务要操作这条记录时,会先比较trx_id和自己的事务ID大小,同时和自己维护的活跃事务列表ID比较,如果不能直接读取记录行的数据,那么会顺着undo日志的"链条"找到自己能用的数据版本,这就是MVCC。

1.2 两个隐藏列

上文已经提到,MySQL每行记录都会有两个隐藏字段,一个是事务ID(trx_id),一个是回滚指针(roll_pointer)。

trx_id :每次一个事务对某条聚簇索引记录改动时,都会把自己的事务id重新写到 trx_id 隐藏列。

roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧版本记录写入到 undo日志中,roll_pointer这个隐藏列是一个地址指针,通过它可以找到该聚簇索引记录历史修改信息。

1.3 undo 日志

undo 日志:又名undo log,也称回滚日志,它是 Innodb 存储引擎层生成的日志。在数据更新之前,MySQL就需要先把更新前的数据记录到 undo log 日志中,当事务回滚时,可以利用 undo log 来进行回滚。

比如现在Tom的账户余额有100,现在有一个事务需要把Tom的账户余额更新为300,大致的流程如下图:

undo log主要分为两种:

insert undo log:顾名思义,此代表执行insert语句时产生的undo log, 它只在事务回滚时需要,因为这种log只是对本事务可见,其他事务不可见,所以当事务提交后,这种类型的undo log就会被系统直接删除回收(也就是该undo log占用的undo页面链表被释放)。

update undo log:事务在进行update或者delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要(也就是MVCC),所以不能在事务提交后马上删除,只在提交后放入undo log的链表,等待purge线程进行最后的删除。

1.4 Read View读视图

什么是Read View?

说白了,Read View就是事务进行快照读操作的时候产生的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统数据以及当前活跃事务的ID(就是启动了还没提交的事务)。

什么是当前读和快照读?

当前读:使用到当前读的场景有select lock in share mode(共享锁)、 select for update 、 update、insert、delete(排他锁)等,这些操作都是一种当前读,因为它需要读取记录的最新版本,而且读取时还有可能会通过加锁保证其他事务不能同时修改当前记录。

快照读:快照读一般指不加锁的select操作,当然如果MySQL数据库的事务隔离级别是串行隔离级别(什么是串行隔离级别?忘了可以重新看这里:还傻傻搞不懂MySQL事务隔离级别么(图文并茂,保证你懂!)),串行级别下的快照读会退化成当前读。

一个Read View 包含以下四个字段:

creator_trx_id :指创建该 Read View 的事务的事务 id。

m_ids :指创建该 Read View 时数据库中所有活跃事务的事务 id 列表,这是一个列表,活跃事务则代表是已启动但未提交的事务。

min_trx_id :指创建 Read View 时数据库中所有活跃事务中最小的事务 id,也就是 m_ids 中的最小值。

max_trx_id :指下一个要创建 Read View 的事务的事务 id,它并不是m_ids中的最大值,需要加以区分。

在可重复读隔离级别下,Read View是在事务开始(begin)之后且执行第一条sql时创建,创建Read View的同时也就生成了一个新的事务id(直到commit结束),事务会依赖该 Read View保证查询结果保持不变直到该事务结束。

二、可重复读实现原理

2.1 什么是可重复读

可重复读(REPEATABLE READ),指在整个事务过程中该事务看到的记录,自始至终都是一样的。

比如现在有两个活跃事务,事务A和事务B,在事务B给Tom账户余额增加100并提交后,事务A能马上看得见么?如下图:

可重复读隔离级别下,事务A在提交前自始至终查到的值都必须一样,所以,也即使事务B更新了Tom的余额也一样。所以余额R1、R2都是100,当事务A提交后再查询(其实是新事务)才能查到新的值,所以R3是200。

之所以有这样的效果,是因为事务A一开始就创建好了Read View,一直到提交前都采用同一个Read View。

2.2 基于多版本实现可重复读

有了以上对隐藏列、undo log和Read View的了解,要分析MVCC的实现原理就简单多了。

假如现在Tom的账户余额有100,当前该记录上的事务id是10。

现在MySQL系统有且只有两个活跃事务,两个事务同时在操作Tom的账户,分别为事务A和事务B。事务A在事务B前启动,但事务A的第一条sql执行前事务B也已启动。

基于以上,事务A和事务B的Read View如下:

为什么两个事务的m_ids都是[11, 12]?因为前面已经说过,RR隔离级别下的Read View是事务内第一个sql语句时才会创建。

在事务 A 的 Read View 中,它的事务 id 是 11,由于是在事务 B 启动后才创建,所以此时活跃的事务id就有 11 和 12,活跃的事务 id 中最小的事务 id 11,下一个事务 id 是 13。

在事务 B 的 Read View 中,因为它后启动于事务A,所以它的事务 id 是 12,当然此时活跃的事务的事务 id 有 11 和 12,活跃的事务 id 中最小的事务 id 还是11,下一个事务 id 依然是 13。

我先把事务A和事务B内部操作流程画出来:

在时间线第③步,事务 A 查询Tom账户余额时,对应记录上的隐藏字段记录着事务id(trx_id) = 10,通过和事务 A 自己本身的 Read View 的 m_ids 字段对比可以发现,trx_id = 10并不在活跃事务的列表中,并且事务 A 的事务 id(11)比它还大,这就可以得出这条记录的事务(trx_id = 10)已经在事务A启动前提交过了,所以该记录直接对事务 A 可见,事务A查询Tom账户余额是100。

接着,在时间线第④步,事务B更新Tom账户余额,给Tom余额增加100,Tom的账户余额变成了200,这时 MySQL 会记录相应的历史版本undo log,并通过隐藏列roll_pointer指向该版本log的地址,形成版本链(更早的undo log则由新的一行undo log指向),如下图:

事务 B 修改了Tom的余额记录,Tom的余额记录就会变成200,同时事务id列(trx_id)记录的是事务B的事务id(12)。

回滚指针列(roll_pointer)则指向新生成的历史版本数据,也就是undo log,这样,最新记录和旧版本记录就通过链表的方式串起来了。

接着,到时间线的第⑤步,事务A继续查询Tom的余额,这时会发现Tom余额记录上的trx_id 为 12,比自己的事务 id(11) 还大,并且12还在活跃事务列表(m_ids)中,也比下一个事务 id(max_trx_id)小。

这时,事务 A 并不会读取这条记录,而是通过roll_pointer找到undo log上的旧版本数据,通过每个版本的roll_pointer一直往下找,直到找到 trx_id 等于或者小于自己的事务id且又不在自己Read View的m_ids列表的第一条记录。

最终,事务 A 读取到 trx_id = 10 的记录,也即查询Tom账户余额还是100。

通过以上的方式MySQL实现了可重复读隔离级别,总而言之,通过多版本并发控制(MVCC),让一个事务在整个事务生命周期内读到的数据保持了一致。

2.3 可重复读可解决幻读么

之前一直都讲,MySQL InnoDB引擎默认的事务隔离级别是可重复读隔离级别,那么既然是这样,可重复读级别解决了幻读问题么?

答案当然是可重复读没有解决幻读问题!

只是MySQL通过一种next-key lock的锁机制一定程度上避免了幻读问题。那MySQL又是如何避免幻读问题的呢?

写到最后发现这是一个需要长篇幅才能说清楚的问题,今天在这里就写啦,欢迎关注我的下一篇文章。

三、结语

我是tin,一个在努力让自己变得更优秀的普通工程师。自己阅历有限、学识浅薄,如有发现文章不妥之处,非常欢迎加我提出,我一定细心推敲并加以修改。

看到这里请安排个“三连”(分享、点赞、在看)再走吧,坚持创作不容易,你的正反馈是我坚持输出的最强动力,谢谢!

最后别忘了关注我哦!公众号【看点代码再上班】

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

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

相关文章

MATLAB | 爱心图例与精致半透明圆角图例

本文中全部示意图均为本人自制,传播时请注明出处。 写了点小成品函数,比如如何绘制饼图时把图例上的图标变成心形: 比如如何自制半透明圆角图例: 依旧先讲原理再给出这俩代码哈: 1 原理讲解-图形对象 封闭四边形对…

3.7.使用cuda核函数加速warpaffine

目录 前言1. warpAffine2. warpAffine案例2.1 导言2.2 main函数2.3 warpaffine_to_center_align函数2.4 warp_affine_bilinear函数2.5 warp_affine_bilinear_kernel核函数2.6 AffineMatrix结构体 3. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0…

【滨小之旅搜索】八皇后

题目传送门 [USACO1.5] 八皇后 Checker Challenge 题目描述 一个如下的 6 6 6 \times 6 66 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有…

js中判断一个对象是否存在

一、Boolean()方法 用Boolean()方法可以将Js中的任意数据类型转为布尔值: 二、用于判断xx是否存在 js一般会自动执行Boolean()方法,我们可以借此判断某个对象在js当前的执行环境中知否存在。如: var x 1; // x的数据类型为数值 if (x) { …

Microsoft 宣布今年底关闭开源软件托管平台 CodePlex

Microsoft 宣布,将关闭开源软件托管平台 CodePlex。Microsoft 2006 年推出这项服务,并决定在今年 12 月 15 日将其关闭。 Microsoft 公司副总裁 Brian Harry 在网上博客中写道,人们将可以下载他们的数据档案,Microsoft 正与面向开…

Vue3 动态路由、动态组件使用示例

前期回顾 Vue3 TS Vite —— 大屏可视化 项目实战_vue3可视化大屏_彩色之外的博客-CSDN博客大屏可视化项目实战_vue3可视化大屏https://blog.csdn.net/m0_57904695/article/details/131014666?spm1001.2014.3001.5501 目录 👍 动态组件 👀 动态路由…

链接做网络互动酷投票平台网络投票

关于微信投票,我们现在用的最多的就是小程序投票,今天的网络投票,在这里会教大家如何用“活动星投票”小程序来进行投票。 我们现在要以“读好书助成长”为主题进行一次投票活动,我们可以在在微信小程序搜索,“活动星投…

3.9.错误处理的理解以及错误的传播特性

目录 前言1. thrust2. error总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习精简 CUDA 教程-错误处理的理解以及错误的传播…

机械臂与RealSense相机手眼标定

环境: 本文主要使用kinova mico机械臂 RealSense D435i深度相机进行了eye to hand的手眼标定。 系统环境:Ubuntu18.04,ROS Melodic 硬件:Kinova mico,RealSense D435i 特别注意:经测试,本方法…

大人,时代变了!做测试也要懂Python开发!(文末送书五本)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

一道有趣的C语言指针笔试题

一道有趣的指针笔试题 注:博主认为,如果想要做对并理解这一题的有关知识点,读者有必要对指针有一个较为全面且深刻的认识,如果小伙伴们对指针还不是太熟悉,建议先看看一篇带你玩转C语言指针:从入门到精通 …

OpenCV 入门教程:自适应阈值处理

OpenCV 入门教程:自适应阈值处理 导语一、自适应阈值处理二、示例应用2.1 图像二值化2.2 图像去噪 总结 导语 自适应阈值处理是图像处理中常用的技术之一,它能够根据图像的局部特征自动调整阈值,从而提高图像的处理效果。在 OpenCV 中&#…

手把手教你云相册项目简易开发 day1 Kafka+IDEA+Springboot+Redis+MySQL+libvips 简单运行和使用

项目的创建 项目采用的是微服务的架构。先创建一个父项目cloud-photo,然后再在module下创建api、image、users的子项目 相关配置: application.yml。此处如果没有redis的话可以先注释掉,因为后面启动需要mysql连接成功和redis服务启动 spr…

多元分类预测 | Matlab 麻雀算法(SSA)优化xgboost的分类预测,多特征输入模型,SSA-xgboost分类预测模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab 麻雀算法(SSA)优化xgboost的分类预测,多特征输入模型,SSA-xgboost分类预测模型 多特征输入单输出的二分类及多分类模型。程序

文心一言 VS 讯飞星火 VS chatgpt (56)-- 算法导论6.3 2题

文心一言 VS 讯飞星火 VS chatgpt (56)-- 算法导论6.3 2题 对于 BUILD-MAX-HEAP 中第 2行的循环控制变量 i 来说,为什么我们要求它是从A.length/2到1递减,而不是从1到A.length/2递增呢? 文心一言: 在 BUILD-MAX-HE…

MySQL每日一练:单表查询

一、首先需要创建一个名为worker的表: mysql> create table worker(-> part_id int(11) not null comment 部门号,-> work_id int(11) not null comment 职工号,-> work_time date not null comment 工作时间,-> salary float(8,2) not null commen…

提升工作效率:推荐几款实用的Mac项目管理工具!

在当今软件和技术高度发达的时代,项目管理依然是一项非常重要的任务。现在,有越来越多的人喜欢使用mac电脑进行项目管理,因为mac众所周知的稳定性和使用便捷性。但问题是,mac系统自带的项目管理工具并不是非常完美,因此…

自定义对象作为HashMap的键,同时重写hashCode和equals方法

如果要将自定义类的实例 作为HashMap的 键,必须重写hashCode和equals方法 简单版本,看不懂看后面复杂版本解释 复杂版本解释 当我们用 HashMap存入自定义的类时,如果不重写这个自定义类的equals和hashCode方法,得到的结果会和我们…

《现代操作系统(中文第四版)》第二章 进程与线程

第二章、进程与线程 操作系统最核心的概念就是进程,这是对正在运行程序的一个抽象。进程是操作系统提供的最古老的也是最重要的抽象概念之一,即使可以使用的cpu只有一个,他们也具有支持并发操作的能力,它们将一个单独的cpu变换成…

【azcopy】

azcopy 下载使用输出 下载 https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10#download-azcopy使用 cd /Users/YJY/Downloads/azcopy_darwin_amd64_10.19.0./azcopy copy https://tapvqacaption.blob.core.windows.net/data/save /Users/YJY/D…