【MySQL】MVCC的实现原理

【MySQL】MVCC的实现原理

  • MVCC简介
  • 事务的隔离级别
    • 读未提交(Read Uncommitted)
      • 概念
      • 分析
    • 读已提交(Read Committed)
      • 概念
      • 分析
      • 结论
    • 可重复读(Repeatable Read)
      • 概念
      • 分析
      • 结论
    • 串行化(Serializable )
      • 概念
      • 结论
  • MVCC实现原理
    • 隐藏字段
    • Undo Log 版本链
    • Read View
    • 判断方法
    • 判断规则
  • 扩展1:当前读和快照读
  • 扩展2:数据库事务的四大特性
  • 小结

MVCC简介

MVCC(Multi-Version Concurrency Control)是一种并发控制机制,用于解决数据库并发访问中,数据一致性问题。它通过在读写操作期间保存同一个数据的多个数据版本,以提供并发事务间的隔离性,从而避免了传统的锁机制所带来的资源争用和阻塞问题。本质上就是一个空间换时间的逻辑。

MVCC是关于事务隔离级别的无锁的实现方式。它用于提高事务的并发性能。

因此,首先来简单介绍下什么是事务的隔离级别,已经了解这一内容的小伙伴可以直接跳过这部分。

事务的隔离级别

并发事务会出现如下三个问题:

  1. 脏读:一个事务A读取到了另一个事务B修改的数据之后,然后这个事务B又进行了回滚操作,从而导致第一个事务A读取的数据是错误的。
  2. 不可重复读:一个事务A两次查询得到的结果不同,因为在两次查询中间,有另一个事务B把数据修改了。
  3. 幻读:一个事务A两次查询中得到的结果集不同,因为在两次查询中另一个事务B又新增了一部分数据。

为了解决并发事务存在的脏读、不可重复读、幻读等问题,数据库大叔设计了四种隔离级别,下面我们用一个例子来分别看看这四个隔离级别。

建表语句:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `age` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

创建一条记录:

mysql> SELECT * FROM user;
+----+-----------------+------+
| id | name            | age  |
+----+-----------------+------+
|  1 | 古时的风筝        |    1 |
+----+-----------------+------+

读未提交(Read Uncommitted)

概念

读未提交隔离级别,限制了两个数据不能同时修改,但任何事务对数据的修改都会第一时间暴露给其他事务,即使事务还没有提交。

分析

下面来做个简单实验验证一下,首先设置全局隔离级别为读未提交。

set global transaction isolation level read uncommitted;

设置完成后,只对之后新起的 session 才起作用,对已经启动 session 无效。如果用 shell 客户端那就要重新连接 MySQL,如果用 Navicat 那就要创建新的查询窗口。

启动两个事务,分别为事务A和事务B,在事务A中使用 update 语句,修改 age 的值为10,初始是1 ,在执行完 update 语句之后,在事务B中查询 user 表,会看到 age 的值已经是 10 了,这时候事务A还没有提交,而此时事务B有可能拿着已经修改过的 age=10 去进行其他操作了。在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,那其实事务B得到的就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的。

顺着时间轴往表示两事务中操作的执行顺序,重点看图中 age 字段的值。
在这里插入图片描述
读未提交,其实就是可以读到其他事务未提交的数据,但没有办法保证你读到的数据最终一定是提交后的数据,如果中间发生回滚,那就会出现脏数据问题,读未提交没办法解决脏数据问题。更别提不可重复读和幻读了,想都不要想。

结论:这级别的事务隔离有脏读、重复读、幻读的问题,基本不会使用。

读已提交(Read Committed)

概念

既然读未提交没办法解决脏数据问题,那么就有了读提交。读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据。那脏数据问题迎刃而解了。
读提交事务隔离级别是大多数流行数据库的默认事务隔离界别,比如 Oracle,但是不是 MySQL 的默认隔离界别。

分析

我们继续来做一下验证,首先把事务隔离级别改为读提交级别。

set global transaction isolation level read committed;

之后需要重新打开新的 session 窗口,也就是新的 shell 窗口才可以。

同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。

这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读提交隔离级别。
在这里插入图片描述
每个 select 语句都有自己的一份快照,而不是一个事务一份,所以在不同的时刻,查询出来的数据可能是不一致的。

结论

读已提交的隔离级别解决了脏读的问题,但还是解决不了不可重复读、幻读问题。

可重复读(Repeatable Read)

概念

可重复是对比不可重复而言的,上面说不可重复读是指同一事物不同时刻读到的数据值可能不一致。而可重复读是指,事务不会读到其他事务对已有数据的修改,及时其他事务已提交,也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。但是,对于其他事务新插入的数据是可以读到的,这也就引发了幻读问题。

分析

同样的,需改全局隔离级别为可重复读级别。

set global transaction isolation level repeatable read;

在这个隔离级别下,启动两个事务,两个事务同时开启。

首先看一下可重复读的效果,事务A启动后修改了数据,并且在事务B之前提交,事务B在事务开始和事务A提交之后两个时间节点都读取的数据相同,已经可以看出可重复读的效果。
在这里插入图片描述
可重复读做到了,这只是针对已有行的更改操作有效,但是对于新插入的行记录,就没这么幸运了,幻读就这么产生了。我们看一下这个过程:

事务A开始后,执行 update 操作,将 age = 1 的记录的 name 改为“风筝2号”;
事务B开始后,在事务执行完 update 后,执行 insert 操作,插入记录 age =1,name = 古时的风筝,这和事务A修改的那条记录值相同,然后提交。

事务B提交后,事务A中执行 select,查询 age=1 的数据,这时,会发现多了一行,并且发现还有一条 name = 古时的风筝,age = 1 的记录,这其实就是事务B刚刚插入的,这就是幻读。
在这里插入图片描述
需要注意的是,当你在 MySQL 中实际测试幻读的时候,并不会出现上图的结果,因为幻读并不会发生,MySQL 的可重复读隔离级别其实解决了幻读问题,它就是间隙锁,感兴趣的小伙伴可以去了解一下,这里就不介绍了。

结论

可重复读隔离级别,解决了赃读、不可重复读和幻读的问题,并且没有用到MySql默认隔离级别

串行化(Serializable )

概念

事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。

结论

串行化隔离级别,可以避免脏读、不可重复读与幻读所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。

小结: 读已提交(RC)可重复读(RR) 的隔离问题就是通过MVCC来实现的。

MVCC实现原理

MVCC 实现的三个关键点:

  • 隐藏字段
  • Undo Log 版本链
  • Read View(读视图或者叫一致性视图)

隐藏字段

对于InnoDB存储引擎,每一行记录除了我们的原数据列之外,还有两个隐藏列trx_idroll_pointer,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列row_id

列名是否必须描述
row_id单调递增的行ID,不是必需的,占用6个字节
trx_id记录操作该数据事务的事务ID
roll_pointer这个隐藏字段相当于一个指针,指向回滚的undo日志

Undo Log 版本链

Undo Log 版本链是 InnoDB 存储引擎中一种重要的数据结构,用于记录数据项的历史修改信息。Undo Log 版本链由多个 Undo Log 记录组成。Undo Log 版本链通过回滚指针将多个 Undo Log 记录连接在一起,形成一个链表结构。每个数据项都维护着自己的 Undo Log 版本链,用于记录该数据项的历史修改信息。主要作用是实现事务的回滚和数据恢复。
在这里插入图片描述

Read View

Read View(读视图)用于管理事务之间数据可见性的一种机制。Read View 在特定时刻为事务创建的一个快照,该快照包含了在该时刻所有未提交事务的事务标识符,以及其他一些辅助信息。(这也就是后面我要和大家说的快照读
在 Read View 中包含了以下 4 个主要的字段:

  • m_ids:当前活跃的事务id集合。
  • min_trx_id:最小活跃事务id。
  • max_trx_id:预分配的事务id,当前最大事务id+1。
  • creator_trx_id:ReadView 创建者的事务id,即当前事务id。

解释:

活跃的事务id:指的是已经创建,但未commit的事务,即处理中的事务id。

RC 级别中,每次快照读都会生成一个全新的 Read View,而 RR 级别中同一个事务会复用一个 Read View。
有了 Read View 和 Undo Log 版本链之后,并发事务在查询数据时就知道要读取哪些数据了。

判断方法

判断方法是根据 Read View 中的 4 个重要字段,先去 Undo Log 版本链中最新的数据行进行比对,如果满足下面 Read View 的判断条件,则返回当前行的数据,如果不满足则继续查找 Undo Log版本链的下一行数据,直到找到满足的条件的数据为止,如果查询完没有满足条件的数据,则返回 NULL。

判断规则

在这里插入图片描述

  • trx_id==creator_trx_id:先将 Undo Log版本链最新数据行中的 trx_id 和 ReadView 中的 creator_trx_id 进行对比,如果他们两个值相同,则说明是在同一个事务中执行,那么直接返回当前 Undo Log版本链的这个数据行即可,如果不相等,则继续下面流程。
  • trx_id<min_trx_id:如果 trx_id 小于 min_trx_id,则说明在执行查询时,其他事务已经提交此行数据了,说明此行数据对所有事务可见,那么直接返回此行数据即可,如果大于等于,则继续下面流程。
  • trx_id>max_trx_id:如果 trx_id 如果大于等于 max_trx_id,则说明当前事务创建之后,才有的当前行数据,那么当前行数据不可见,继续执行后续流程。
  • min_trx_id<=trx_id<max_trx_id:trx_id 在 min_trx_id 和 max_trx_id 之间还分为以下两种情况:
    • trx_id 在 m_ids 中:说明事务尚未执行完,该行数据不可被访问。
    • trx_id 未在 m_ids 中:说明事务已经执行完,可以返回该行数据。

对于删除的情况,会将版本链的最新数据复制一份,然后将trx_id修改成删除操作的trx_id,同时会在记录的头信息的标记位(delete_flag)上设置true,用来表示已经删除,在查询时,如果对应记录的delete_flag为true,则表示已经被删除,就不会返回数据。

以上判断规则从 Undo Log版本链最新的行数据,逐行对比,直到找到匹配的数据,否则查询完未匹配上,则返回 NULL。

扩展1:当前读和快照读

快照读:是指在一个事务中,读取的数据版本是在事务开始时已经存在的数据版本,而不是最新的数据版本。这种读取方式提供了事务在执行期间看到的数据视图的一致性,select 查询就是快照读。

当前读:是指在事务中读取最新的数据版本,以下几种操作都是快照读:

  1. select lock in share mode(共享锁)
  2. select for update(排他锁)
  3. update(排他锁)
  4. insert(排他锁)
  5. delete(排他锁)

扩展2:数据库事务的四大特性

  1. A(Atomicity):原子性
    概念:原子性是指事务中的所有操作要么全部成功,要么全部失败,回滚到事务开始之前的状态。例如,一个银行转账操作,要么钱成功从A账户转到B账户,要么转账操作完全失败,A账户和B账户的余额都不发生变化。
    实现:
    • undo log
  2. C(Consistency):一致性
    概念:一致性是指事务必须将数据库从一个一致性状态转换为另一个一致性状态。例如,一个银行转账操作,必须保证A账户的余额减少的金额等于B账户的余额增加的金额。
    实现:
    • 原子性、隔离性和持久性共同实现
  3. I(Isolation):隔离性
    概念:隔离性是指多个并发执行的事务彼此互不影响,每个事务的执行结果就像是在数据库中单独执行一样。例如,两个用户同时向同一个账户转账,不会出现转账金额丢失或者账户余额错误的情况。
    实现:
    • 锁机制(当前读)
    • MVCC(快照读)
  4. D(Durability):持久性
    概念:持久性是指一旦事务提交,其对数据库所做的修改就将永久保存,即使数据库发生故障,也不会丢失。例如,一个银行转账操作,一旦成功提交,即使数据库服务器宕机,转账操作也不会回滚。
    实现:
    • redolog

undo log 和 redo log 是相互协作的,共同保证了事务的ACID特性。

小结

  • MVCC就是为快照读而生的,维护不同的快照版本,使得不同事务的读-写操作不会冲突,实现多版本并发控制,从而提高性能。

  • MVCC 主要应用于 InnoDB 引擎中的 RC 事务隔离级别和 RR 隔离级别,其中 RC 隔离级别每次快照读都会生成一个新的 Read View,而 RR 隔离级别只在第一次快照读时生成 Read View,之后会复用 Read View,从而解决了(部分)幻读问题。

  • MVCC是一种用于解决数据库并发问题的乐观锁技术,多版本并发控制通过保存数据在某个时间点的快照来实现。换句话说,读操作不会阻塞写操作,写操作也不会阻塞读操作,以此来提高数据库性能。在每次对数据的操作,都在undo log版本链中进行。

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

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

相关文章

实战—登录功能引发的逻辑漏洞

密码找回功能可能存在的漏洞 1.验证码发送后前端返回 2.验证码无次数限制可爆破 3.验证码可控/邮箱篡改为自己的接收短信验证码/手机号码篡改为自己的接收短信验证码 4.越权漏洞—>自己验证码通过改包然后修改他们密码 5.任意用户密码重置 6.密保问题在前端源码 实战…

Linux基础——Linux开发工具(上)_vim

前言&#xff1a;在了解完Linux基本指令和Linux权限后&#xff0c;我们有了足够了能力来学习后面的内容&#xff0c;但是在真正进入Linux之前&#xff0c;我们还得要学会使用Linux中的几个开发工具。而我们主要介绍的是以下几个&#xff1a; yum, vim, gcc / g, gdb, make / ma…

49. 字母异位词分组 128. 最长连续序列

49. 字母异位词分组 128. 最长连续序列 把集合里面的所有元素都放入set容器里面 定义结果最大连续数量 ans for循环遍历每个元素 先判断集合里面有没有比这个元素小1的 如果没有 说明这个元素就是序列的第一个元素 然后接着找集合里面有没有比这个元素大1的 while一直找 …

牛客NC353 回文子串的数量【中等 字符串,枚举,回文 C++/Java/Go/PHP 高频】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/3e8b48c812864b0eabba0b8b25867738 思路 参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*** param str string字符串…

详解centos8 搭建使用Tor 创建匿名服务和匿名网站(.onion)

1 Tor运行原理&#xff1a; 请求方需要使用&#xff1a;洋葱浏览器&#xff08;Tor Browser&#xff09;或者Google浏览器来对暗&#xff0c;网网站进行访问 响应放需要使用&#xff1a;Tor协议的的Hidden_service 2 好戏来了 搭建步骤&#xff1a; 1.更新yum源 rpm -Uvh h…

OceanBase V4.3 发布—— 迈向实时分析 AP 的重要里程

OceanBase在2023年初&#xff0c;发布了4.x架构的第一个重要版本&#xff0c;V4.1。该版本采用了单机分布式一体化架构&#xff0c;并在该架构的基础上&#xff0c;将代表数据库可靠性的RTO降低至 8 秒以内&#xff0c;从而确保在意外故障发生后&#xff0c;系统能够在极短时间…

javafx如何一键打包成exe

javafx如何打包成exe JavaFX-Template-Native 集成jfoenix、commons-math、commons-lang3、netty&#xff0c;方便一些和底层做通信使用&#xff0c;不需要可以自行pom中去掉依赖当前使用的jdk17&#xff0c;理论上jdk14都支持采用模块化&#xff0c;支持一键打包生成很小的ex…

Threejs制作服务器机房冷却结构

这节再绘制一个机房的结构&#xff0c;因为内容比较简单&#xff0c;就只使用一个章节来介绍&#xff0c; 先来一张效果图&#xff0c; 需要两个模型&#xff1a;一个冷却设备&#xff0c;一个服务器机箱&#xff0c;我这里是从网上找来的&#xff0c;首先我们搭建一个场景&a…

solidworks出现slderrresu.dll错误如何解决?亲测有效

通过近来给客户安装SolidWorks发现&#xff0c;SolidWorks2010、SolidWorks2012、SolidWorks2014、SolidWorks2015、SolidWorks2016、SolidWorks2017都会出现这个slderrresu.dll安装错误问题&#xff1a; 其实这个错误很好解决,主要是因為安裝中文版solidworks沒有選擇安裝中文…

.NET操作 Access (MSAccess)

注意&#xff1a;新项目推荐 Sqlite &#xff0c;Access需要注意的东西太多了&#xff0c;比如OFFICE版本&#xff0c;是X86还是X64 连接字符串 ProviderMicrosoft.ACE.OleDB.15.0;Data Source"GetCurrentProjectPath"\\test.accdb//不同的office版本 连接字符串有…

Linux 虚拟主机切换php版本及参数

我使用的Hostease的Linux虚拟主机产品,由于网站程序需要支持高版本的PHP,程序已经上传到主机&#xff0c;但是没有找到切换PHP以及查看PHP有哪些版本的位置&#xff0c;因此咨询了Hostease的技术支持&#xff0c;寻求帮助了解到可以实现在cPanel面板上找到此切换PHP版本的按钮&…

亚马逊商品详情API接口:解锁亚马逊商品信息的全面视野

亚马逊商品详情API接口&#xff1a;解锁亚马逊商品信息的全面视野 在跨境电商和电商数据分析领域&#xff0c;亚马逊作为全球领先的电商平台&#xff0c;其商品信息对商家、开发者以及市场分析师来说至关重要。为了更高效地获取亚马逊平台上的商品详情&#xff0c;亚马逊商品详…

哨兵-1A与DInSAR技术监测尼泊尔地震前后地表形变

辽宁抚顺是一座以煤而兴的重工业城市&#xff0c;建国初期抚顺被誉为“煤都”&#xff0c;这里有闻名全国享誉世界的亚洲最大的露天煤矿——抚顺西露天矿。抚顺西露天矿地处抚顺煤田西部&#xff0c;矿坑东西长6.6公里&#xff0c;南北宽2.2公里&#xff0c;最终开采垂直深度47…

释放人工智能潜力,Polkadot 再掀区块链技术革命

来源&#xff1a;https://polkadot.network/blog/unleashing-the-potential-of-ai-with-polkadot/ 编译&#xff1a;OneBlock 区块链技术开辟了一个充满可能性的世界&#xff0c;这一点在新兴崛起的人工智能&#xff08;AI&#xff09;领域最为明显。 Polkadot 生态处于这场…

Go Web开发【xorm 框架】

1、xorm 1.1、xorm 简介 xorm 是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 特性 支持 struct 和数据库表之间的灵活映射&#xff0c;并支持自动同步事务支持同时支持原始SQL语句和ORM操作的混合执行使用连写来简化调用支持使用ID, In, Where, Limit,…

【JavaWeb Day 2 - JS 】

JavaWeb Day 2 - JS JS背景故事1. JS 引入方式2. JS 基本语法2.2 变量2.3 数据类型2.4 运算符 3. JS 函数4. JS 对象4.1 Array对象4.2 String对象4.3 JSON对象4.4 BOM对象4.4.1 windows 对象4.4.2 location 对象 4.5 DOM 对象DOM 案例 5. JS 事件监听5.1 JS 事件绑定 及 常见事…

JavaScript底层原理(栈、堆、主线程、任务队列、事件循环机制)

1. 栈(heap)和堆(stack) 栈是栈内存的简称&#xff0c;堆是堆内存的简称。顾名思义&#xff0c;内存是干啥的&#xff1f;内存就是用来存放数据的。 栈 栈只有一个入口&#xff0c;同时也是出口&#xff0c;数据遵循先进后出、后进先出的原则。 栈用于存放基本类型数据和引用…

vue 模板字符串

1.模板字符串换行问题 white-space: pre-wrap; 2. 鼠标移入 显示提示框 点击手动隐藏 myChart.on("mouseover", function (params) {myChart.dispatchAction({type: "downplay",}); }); tooltip: {show: true, //是否显示提示框组件&#xff0c;包括…

安卓获取SHA

1&#xff1a;安卓通过签名key获取SHA 方式有两种&#xff0c; 1、电脑上来存在eclipse的用户或正在使用此开发工具的用户就简单了&#xff0c;直接利用eclipse 走打包流程&#xff0c;再打包的时候选择相应的签名&#xff0c;那么在当前面板的下面便会出现签名的相关信息。 2、…

牛客热题:合并K个升序链表

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;合并K个升序链表题目链接&#…