mysql取消mvvc机制_MySQL探秘(六):InnoDB一致性非锁定读

一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB会去读取行的一个快照。

47af9a05ce9c25a7eaaa5b5539ad5263.png

上图直观地展现了InnoDB一致性非锁定读的机制。之所以称其为非锁定读,是因为不需要等待行上排他锁的释放。快照数据是指该行的之前版本的数据,每行记录可能有多个版本,一般称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control, MVVC)。InnoDB是通过undo log来实现MVVC。undo log本身用来在事务中回滚数据,因此快照数据本身是没有额外开销。此外,读取快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改操作。

一致性非锁定读是InnoDB默认的读取方式,即读取不会占用和等待行上的锁。但是并不是在每个事务隔离级别下都是采用此种方式。此外,即使都是使用一致性非锁定读,但是对于快照数据的定义也各不相同。

在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB使用一致性非锁定读。然而,对于快照数据的定义却不同。在READ COMMITTED事务隔离级别下,一致性非锁定读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别下,则读取事务开始时的行数据版本。

我们下面举个例子来详细说明一下上述的情况。

# session A

mysql> BEGIN;

mysql> SELECT * FROM test WHERE id = 1;

我们首先在会话A中显示地开启一个事务,然后读取test表中的id为1的数据,但是事务并没有结束。于此同时,用户在开启另一个会话B,这样可以模拟并发的操作,然后对会话B做出如下的操作:

# session B

mysql> BEGIN;

mysql> UPDATE test SET id = 3 WHERE id = 1;

在会话B的事务中,将test表中id为1的记录修改为id=3,但是事务同样也没有提交,这样id=1的行其实加了一个排他锁。由于InnoDB在READ COMMITTED和REPEATABLE READ事务隔离级别下使用一致性非锁定读,这时如果会话A再次读取id为1的记录,仍然能够读取到相同的数据。此时,READ COMMITTED和REPEATABLE READ事务隔离级别没有任何区别。

2123d2a53e7ac87059b1f1626cc485b8.png

如上图所示,当会话B提交事务后,会话A再次运行SELECT * FROM test WHERE id = 1的SQL语句时,两个事务隔离级别下得到的结果就不一样了。

对于READ COMMITTED的事务隔离级别,它总是读取行的最新版本,如果行被锁定了,则读取该行版本的最新一个快照。因为会话B的事务已经提交,所以在该隔离级别下上述SQL语句的结果集是空的。

对于REPEATABLE READ的事务隔离级别,总是读取事务开始时的行数据,因此,在该隔离级别下,上述SQL语句仍然会获得相同的数据。

MVVC

我们首先来看一下wiki上对MVVC的定义:

Multiversion concurrency control (MCC or MVCC), is a concurrency control

method commonly used by database management systems to provide

concurrent access to the database and in programming languages to

implement transactional memory.

由定义可知,MVVC是用于数据库提供并发访问控制的并发控制技术。

数据库的并发控制机制有很多,最为常见的就是锁机制。锁机制一般会给竞争资源加锁,阻塞读或者写操作来解决事务之间的竞争条件,最终保证事务的可串行化。而MVVC则引入了另外一种并发控制,它让读写操作互不阻塞,每一个写操作都会创建一个新版本的数据,读操作会从有限多个版本的数据中挑选一个最合适的结果直接返回,由此解决了事务的竞争条件。

考虑一个现实场景。管理者要查询所有用户的存款总额,假设除了用户A和用户B之外,其他用户的存款总额都为0,A、B用户各有存款1000,所以所有用户的存款总额为2000。但是在查询过程中,用户A会向用户B进行转账操作。转账操作和查询总额操作的时序图如下图所示。

bd9c82be7c27beb4e737a9c5c7cafbf8.png

如果没有任何的并发控制机制,查询总额事务先读取了用户A的账户存款,然后转账事务改变了用户A和用户B的账户存款,最后查询总额事务继续读取了转账后的用户B的账号存款,导致最终统计的存款总额多了100元,发生错误。

使用锁机制可以解决上述的问题。查询总额事务会对读取的行加锁,等到操作结束后再释放所有行上的锁。因为用户A的存款被锁,导致转账操作被阻塞,直到查询总额事务提交并将所有锁都释放。

f8f98b0a446a189b597c7d41623e5dd3.png

但是这时可能会引入新的问题,当转账操作是从用户B向用户A进行转账时会导致死锁。转账事务会先锁住用户B的数据,等待用户A数据上的锁,但是查询总额的事务却先锁住了用户A数据,等待用户B的数据上的锁。

使用MVVC机制也可以解决这个问题。查询总额事务先读取了用户A的账户存款,然后转账事务会修改用户A和用户B账户存款,查询总额事务读取用户B存款时不会读取转账事务修改后的数据,而是读取本事务开始时的数据副本(在REPEATABLE READ隔离等级下)。

dbfa09fabd58cfa70a9c43e6f907ddfa.png

MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力。借助MVCC,数据库可以实现READ COMMITTED,REPEATABLE READ等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了ACID中的I特性(隔离性)

InnoDB的MVVC实现

多版本并发控制仅仅是一种技术概念,并没有统一的实现标准, 其的核心理念就是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。虽然字面上是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。

数据库的事务有时需要进行回滚操作,这时就需要对之前的操作进行undo。因此,在对数据进行修改时,InnoDB会产生undo log。当事务需要进行回滚时,InnoDB可以利用这些undo log将数据回滚到修改之前的样子。

根据行为的不同 undo log 分为两种 insert undo log和update undo log。

insert undo log 是在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对于其它事务此记录是不可见的,所以 insert undo log 可以在事务提交后直接删除而不需要进行 purge 操作。

update undo log 是 update 或 delete 操作中产生的 undo log,因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此 update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。

为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用回滚段的方式来维护undo log的并发写入和持久化。回滚段实际上是一种 Undo 文件组织方式。

InnoDB行记录有三个隐藏字段:分别对应该行的rowid、事务号db_trx_id和回滚指针db_roll_ptr,其中db_trx_id表示最近修改的事务的id,db_roll_ptr指向回滚段中的undo log。如下图所示。

c070b51c33d1f572c6c73ae6f7f54e73.png

当事务2使用UPDATE语句修改该行数据时,会首先使用排他锁锁定改行,将该行当前的值复制到undo log中,然后再真正地修改当前行的值,最后填写事务ID,使用回滚指针指向undo log中修改前的行。如下图所示。

2bcd8cd0c092cc7df26b5e4132a5ff85.png

当事务3进行修改与事务2的处理过程类似,如下图所示。

9882cdccd181eb66f6048c8a642d519a.png

REPEATABLE READ隔离级别下事务开始后使用MVVC机制进行读取时,会将当时活动的事务id记录下来,记录到Read View中。READ COMMITTED隔离级别下则是每次读取时都创建一个新的Read View。

Read View是InnoDB中用于判断记录可见性的数据结构,记录了一些用于判断可见性的属性。

low_limit_id:某行记录的db_trx_id < 该值,则该行对于当前Read View是一定可见的

up_limit_id:某行记录的db_trx_id >= 该值,则该行对于当前read view是一定不可见的

low_limit_no:用于purge操作的判断

rw_trx_ids:读写事务数组

Read View创建后,事务再次进行读操作时比较记录的db_trx_id和Read View中的low_limit_id,up_limit_id和读写事务数组来判断可见性。

如果该行中的db_trx_id等于当前事务id,说明是事务内部发生的更改,直接返回该行数据。否则的话,如果db_trx_id小于up_limit_id,说明是事务开始前的修改,则该记录对当前Read View是可见的,直接返回该行数据。

如果db_trx_id大于或者等于low_limit_id,则该记录对于该Read View一定是不可见的。如果db_trx_id位于[up_limit_id, low_limit_id)范围内,需要在活跃读写事务数组(rw_trx_ids)中查找db_trx_id是否存在,如果存在,记录对于当前Read View是不可见的。

如果记录对于Read View不可见,需要通过记录的DB_ROLL_PTR指针遍历undo log,构造对当前Read View可见版本数据。

简单来说,Read View记录读开始时及其之后,所有的活动事务,这些事务所做的修改对于Read View是不可见的。除此之外,所有其他的小于创建Read View的事务号的所有记录均可见。

后记

我们后续还会学习InnoDB的锁的相关的知识,请大家持续关注。

76fdc81068e428153d3d7e42ea177d6e.png

参考文章

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

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

相关文章

APP应用 HTTP/1.0中keep-alive

在HTTP/1.0中keep-alive不是标准协议&#xff0c;客户端必须发送Connection:Keep-Alive来激活keep-alive连接。https://www.imooc.com/article/31231HTTP协议是无状态的协议&#xff0c;即每一次请求都是互相独立的。因此它的最初实现是&#xff0c;每一个http请求都会打开一个…

安装mysql8._安装MySQL8(附详细图文)

安装MySQL8(附详细图文)删除mysql服务&#xff1a;mysqld -remove mysql1、下载 mysql 8下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/2、配置 mysql 配置文件打开 mysql 8 的安装目录&#xff1a;my.ini注意设置自己对应的 mysql 安装目录 和数据存放目录[mysq…

win10安装windows live writer 错误:OnCatalogResult:0x80190194

到官网下载了一个在线安装程序&#xff0c;可是一运行就提示无法安装&#xff0c;显式错误“OnCatalogResult:0x80190194”&#xff0c;如下图所示 找到windows live安装程序的安装日志文件。具体位置是&#xff1a;C:\Users\All Users\Microsoft\WLSetup\Logs 需要下载安装文件…

TZOJ--5480: 孤衾易暖 // POJ--3735 Training little cats (矩阵快速幂)

5480: 孤衾易暖 时间限制(普通/Java):1000MS/3000MS 内存限制:65536KByte 描述 哇&#xff0c;好难&#xff0c;我要放弃了(扶我起来&#xff0c;我还能A 寒夜纵长&#xff0c;孤衾易暖&#xff0c;钟鼓渐清圆。 生活也许有些不如意的地方&#xff0c;但是没有什么是拥有一…

IntelliJ IDEA2017 修改缓存文件的路径

IDEA的缓存文件夹.IntelliJIdea2017.1&#xff0c;存放着IDEA的破解密码&#xff0c;各个项目的缓存&#xff0c;默认是在C盘的用户目录下&#xff0c;目前有1.5G大小。现在想要把它从C盘移出。 在IDEA的安装路径下中&#xff0c;进入bin目录后找到属性文件&#xff1a;idea.pr…

python字符串后面添加字符串_什么是字符串?怎样在Python中添加字符串?

字符串是一种表示文本的数据类型&#xff0c;字符串中的字符可以是ASCII字符、各种符号以及各种Unicode字符。Python中的字符串有如下三种表现方式。第1种方式&#xff1a;使用单引号包含字符。示例代码如下&#xff1a;a 123注意&#xff0c;单引号表示的字符串里不能包含单引…

surround360

1.读入配置文件2.创建底部和顶部投影线程3.将侧面图投影到球座标(1)load侧面相机图像(2)创建投影线程(3)等待线程结束4.渲染立体全景图(侧边)(1)计算重叠区域宽度(2)创建准备生成新视图的线程: 送入相邻两个相机的投影图,计算光流flowLtoR,flowRtoL, 保存在novelViewGenerators…

Docker安装java-Zookeeper进行操作

Docker安装Zookeeper下载Zookeeper镜像 docker pull zookeeper启动容器并添加映射 docker run --privilegedtrue -d --name zookeeper --publish 2181:2181 -d zookeeper:latest 查看容器是否启动 docker ps idea提供了一个Zookeeper插件&#xff0c;以供连接Zookeeper服务中心…

C# 装箱和拆箱

C#的值类型可以分为在栈上分配内存的值类型和在托管堆上分配内存的引用类型。 1、那么值类型和引用类型能否相互转换呢? 答案是肯定的,C#通过装箱和拆箱来实现两者的相互转换。 (1)、装箱 ---把值类型强制转换成引用类型(object类型) (2)、拆箱 ---把引用类型强制转换成值…

node中的Stream-Readable和Writeable解读

在node中&#xff0c;只要涉及到文件IO的场景一般都会涉及到一个类&#xff0d;Stream。Stream是对IO设备的抽象表示&#xff0c;其在JAVA中也有涉及&#xff0c;主要体现在四个类&#xff0d;InputStream、Reader、OutputStream、Writer&#xff0c;其中InputStream和OutputSt…

SQL Server读写分离之发布订阅

一、发布 上面有多种发布方式&#xff0c;这里我选择事物发布&#xff0c;具体区别请自行百度。 点击下一步、然后继续选择需要发布的对象。 如果需要筛选发布的数据点击添加。 根据自己的计划选择发布的时间。 点击安全设置&#xff0c;设置代理信息。 最后单击完成系统会自动…

码农和程序员的几个重要区别!

如果一个企业老板大声嚷嚷说&#xff0c;“我要招个程序员”&#xff0c;那么十之八九指的是“码农”——一种纯粹为了钱而写代码的技术人员。这其实是一种非常狭隘和错误的做法&#xff0c;原因么&#xff0c;且听我一一道来。1、码农写代码&#xff0c;程序员写系统从本质上讲…

移动端工程架构与后端工程架构的思想摩擦之旅(1)

此文已由作者黎星授权网易云社区发布。欢迎访问网易云社区&#xff0c;了解更多网易技术产品运营经验记资源投放后端工程的架构调整与优化 架构思考一直以来对软件工程架构有着极大的兴趣&#xff0c;无论是之前负责的移动端Android工程&#xff0c;亦或是现在转到后端开发后维…

logging记录日志

日志是一个系统的重要组成部分&#xff0c;用以记录用户操作、系统运行状态和错误信息。日志记录的好坏直接关系到系统出现问题时定位的速度。logging模块Python2.3版本开始成为Python标准库的一部分。 日志级别 在最简单的使用中&#xff0c;我们直接导入logging模块&#xff…

C#编程之接口

1.定义 接口是把公共方法和属性组合起来&#xff0c;以封装特定功能的一个集合。&#xff08;一旦定义了接口&#xff0c;就可以在类中实现它。这样类就可以支持接口所指定的所有属性和成员&#xff09; 注意1&#xff1a;接口不能单独存在。不能像实例化一个类那样实例化一个接…

supervisor守护进程

2019独角兽企业重金招聘Python工程师标准>>> supervisor 是一个client/server系统,把不是守护进程的进程变成守护进程,并监控和控制类 Unix 操作系统上的进程。 upervisor就是用Python开发的一套通用的进程管理程序&#xff0c;能将一个普通的命令行进程变为后台dae…

【学习笔记】深入理解js原型和闭包(11)——执行上下文栈

继续上文的内容。 执行全局代码时&#xff0c;会产生一个执行上下文环境&#xff0c;每次调用函数都又会产生执行上下文环境。当函数调用完成时&#xff0c;这个上下文环境以及其中的数据都会被消除&#xff0c;再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个…

Java基础--访问权限控制符

今天我们来探讨一下访问权限控制符。 使用场景一&#xff1a;攻城狮A编写了ClassA&#xff0c;但是他不想所有的攻城狮都可以使用该类&#xff0c;应该怎么办&#xff1f; 使用场景二&#xff1a;攻城狮A编写了ClassA&#xff0c;里面有func1方法和func2方法&#xff0c;但是他…

Dubbo简单介绍及实例

1、概念 Dubbo是一个分布式服务框架&#xff0c;以及阿里巴巴内部的SOA服务化治理方案的核心框架。其功能主要包含&#xff1a;高性能NIO通讯及多协议集成。服务动态寻址与路由。软负载均衡与容错&#xff0c;依赖分析与降级等。 说通俗点&#xff0c;就是首先将程序组件化成一…

bzoj1116: [POI2008]CLO

传送门&#xff1a;http://www.lydsy.com/JudgeOnline/problem.php?id1116 题目大意&#xff1a;Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 你要把其中一些road变成单向边使得&#xff1a;每个town都有且只有一个入度 题解&am…