MySQL 多版本并发控制 MVCC

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.mzph.cn/news/627302.shtml

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

相关文章

.NET领域最硬核的gRPC 核心能力一把梭

前言&#xff0c;本文定位为.NET方向 grpc核心能力一把梭&#xff0c;全篇是姿势性和结论性的展示&#xff0c; 方便中高级程序员快速上手.NET Grpc。 有关grpc更深层次的前世今生、底层原理、困惑点释疑请听下回分解&#xff0c; 欢迎菜鸟老鸟们提出宝贵意见。 grpc宏观目标&…

跨平台进程/任务管理服务——Meproc的配置

配置 Meproc的配置非常简单&#xff0c;只有以下几个配置选项。 Conf [ip: 0.0.0.0,port: 8606,log_level: debug,log_dir: /tmp,web: [ip: 127.0.0.1,port: 8606,],bootstrap_cmd: , ];ip 是 Meproc 服务监听 HTTP 请求的地址。port 是Meproc服务监听HTTP请求的端口。log_l…

「Vue3面试系列」Vue 3.0中如果想实现一个 Modal组件应该怎么设计?

文章目录 一、组件设计二、需求分析三、实现流程目录结构组件内容实现 API 形式事件处理其他完善 一、组件设计 组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念&#xff08;组件&#xff09;来实现开发的模式 现在有一个场景&#xff0c;点击新增与编辑都弹框出来进…

51单片机学习总结(自学)

1、模块化编程 c语言模块化编程实现思路设计代码 具体的程序实现代码如下所示 1&#xff1a;程序的头文件 2&#xff1a;程序的函数文件 3&#xff1a;程序的主文件控制函数的实现 持续更新中......

用el-image-viewer实现全局预览图片

背景 在后台管理系统中&#xff0c;一些预览图片的场景&#xff0c;通常都是使用 el-image-viewer 去实现&#xff0c;但是如果多个地方都需要预览图片&#xff0c;又要重复的去写 el-image-viewer 以及一些重复的和预览相关的代码。 可以把预览图片的组件放在根文件&#x…

MyTinySTL 简单分析(一)--iterator.h

MyTinySTL 简单分析 目前在学习STL&#xff0c;看到一个开源的项目MyTinySTL&#xff0c;非常不错。想着照着这个代码自己敲一遍应该也能有些进步。然后就开始了学习过程。 首先分析的是vector 以下是由vector.h关联的所有头文件 其中有几个文件是重复的&#xff0c;例如type…

智能光栅光片显微成像技术的LabVIEW解决方案

智能光栅光片显微成像技术的LabVIEW解决方案 在生物医学研究中&#xff0c;高效的成像技术对于捕捉细胞内罕见和复杂事件至关重要。智能光栅光片显微技术&#xff08;smartLLSM&#xff09;的出现&#xff0c;代表了LabVIEW软件在高端成像领域的革命性应用&#xff0c;这项技术…

solr 远程命令执行漏洞复现 (CVE-2019-17558)

solr 远程命令执行漏洞复现 (CVE-2019-17558) ‍ 名称: solr 远程命令执行 (CVE-2019-17558) 描述: Apache Velocity是一个基于Java的模板引擎&#xff0c;它提供了一个模板语言去引用由Java代码定义的对象。Velocity是Apache基金会旗下的一个开源软件项目&#xff0c;旨在确…

Qt 快捷键设置

以 “在编辑时自动补齐”快捷键 为例&#xff1a; 位置&#xff1a;红色 搜索快捷键&#xff1a;蓝色 修改方式&#xff1a;绿色 快捷键&#xff1a;黄色

22k+star炒鸡好用的开源的网盘神器FileBrowser Docker自建个人网盘神器教程

目录 简介 1.拉取镜像 2.创建并启动容器 2.1创建目录 2.2启初始化一个容器用于导出配置文件和数据库&#xff0c;只挂载数据目录 2.3先将数据库文件和配置文件复制出来 2.4停止容器并删除容器 2.5创建完整的容器 3.愉快地使用 3.1示例&#xff1a; 3.2图片预览 3.3json…

基于Java SSM框架实现学生综合考评管理系统项目【项目源码+论文说明】

基于java的SSM框架实现学生学生综合考评管理系统演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 学生综合考评管理系统&#xff0c;主要的模块包括查看&#xff1b;管理员&#xff1b;个…

鸿蒙HarmonyOS实战-工具安装和Helloworld案例

&#x1f680;前言 HarmonyOS是华为自主开发的操作系统&#xff0c;它在2020年9月正式发布。它最初被称为鸿蒙OS&#xff0c;后来更名为HarmonyOS。HarmonyOS旨在提供一种可在各种设备上无缝运行的统一操作系统&#xff0c;包括智能手机、平板电脑、智能穿戴设备、智能音箱、车…

“核弹级“攻击队视角下的监管痛点解决方案

痛点分析及解决方案 一、辖区企业资产分散且不透明 - 传统的监管体系中&#xff0c;政府监管单位往往面临着辖区企业资产分散且不透明的问题。 - 企业无法梳理自身资产&#xff0c;上报的资产台账无法涵盖全部自身资产 - 监管单位精力有限&#xff0c;无法保证辖区企业资产台账…

工信部颁发的人工智能证书《自然语言与语音处理设计开发工程师》证书到手啦!

工信部颁发的人工智能证书《自然语言与语音处理设计开发工程师》证书拿到手啦&#xff01; 近期正在报考的工信部颁发的人工智能证书还有&#xff1a; 《计算机视觉处理设计开发工程师》中级 2024年1月24日至28日-北京 《自然语言与语音处理设计开发工程师》中级 第二期 20…

高级分布式系统-第15讲 分布式机器学习--分布式机器学习算法

高级分布式系统汇总&#xff1a;高级分布式系统目录汇总-CSDN博客 分布式机器学习算法 按照通信步调&#xff0c;大致可以分为同步算法和异步算法两大类。 同步算法下&#xff0c;通信过程中有一个显式的全局同步状态&#xff0c;称之为同步屏障。当工作节点运行到同步屏障 …

定位Linux下写句柄错误问题

编写代码时发现对于以下can 写资源句柄时出现的错误不是很能分析&#xff0c;但是如果将错误码打印出来后&#xff0c;清晰了很多&#xff0c;并且还可以查看。 int Can::Write(const struct can_frame *send_frame) { int result;std::cout << "before write s_ :…

JUC——8锁问题

文章目录 问题一和问题二&#xff1a;问题三&#xff1a;问题四&#xff1a;问题五和问题六&#xff1a;问题七和问题八&#xff1a;总结 八锁现象是在多线程编程中经常遇到的一种情况&#xff0c;指的是在使用synchronized关键字进行同步时可能出现的不同的执行顺序。下面对问…

C++ 类的静态成员

我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时&#xff0c;这意味着无论创建多少个类的对象&#xff0c;静态成员都只有一个副本。 静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句&#xff0c;在创建第一个对象时&#xff0…

JNPF低代码引擎到底是什么?

最近听说一款可以免费部署本地进行试用的低代码引擎&#xff0c;源码上支持100%源码&#xff0c;提供的功能和技术支持比较完善。借助这篇篇幅我们了解下JNPF到底是什么&#xff1f; JNPF开发平台是一款PaaS服务为核心的零代码开发平台&#xff0c;平台提供了多租户账号管理、主…

Linux中的定时任务(案例:定时备份和清空)

前言 Linux中的定时任务&#xff08;案例&#xff1a;定时备份和清空&#xff09; crontab 命令 Linux crontab 是用来定期执行程序的命令, 当安装完成操作系统之后&#xff0c;默认便会启动此任务调度命令。crond 命令每分钟会定期检查是否有要执行的工作&#xff0c;如果有…