MVCC详解

article/2025/11/9 23:16:29

一、前言

全称Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。以下文章都是围绕InnoDB引擎来讲,因为myIsam不支持事务。

同一行数据平时发生读写请求时,会上锁阻塞住。但mvcc用更好的方式去处理读—写请求,做到在发生读—写请求冲突时不用加锁

这个读是指的快照读,而不是当前读,当前读是一种加锁操作,是悲观锁

那它是怎么做到读—写不加锁的呢,快照读和当前读又是什么呢?下面就为大家一一介绍。

二、正文

1.什么是当前读?

它读取的数据库记录,都是当前最新版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。

如下操作都是当前读:

  • select lock in share mode (共享锁)
  • select for update (排他锁)
  • update (排他锁)
  • insert (排他锁)
  • delete (排他锁)
  • 串行化事务隔离级别

2.什么是快照读?

快照读的实现是基于多版本并发控制,即MVCC,既然是多版本,那么快照读读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。

如下操作是快照读:

  • 不加锁的select操作(注:事务级别不是串行化)

3.快照读和MVCC关系?

MVCCC是“维持一个数据的多个版本,使读写操作没有冲突”的一个抽象概念

这个概念需要具体功能去实现,这个具体实现就是快照读。(具体实现下面讲)

4.数据库并发场景?

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
  • 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

5.MVCC解决并发哪些问题?

mvcc用来解决读—写冲突的无锁并发控制,就是为事务分配单向增长时间戳。为每个数据修改保存一个版本,版本与事务时间戳相关联

读操作只读取该事务开始前数据库快照

解决问题如下:

  • 并发读-写时:可以做到读操作不阻塞写操作,同时写操作也不会阻塞读操作。
  • 解决脏读幻读不可重复读等事务隔离问题,但不能解决上面的写-写 更新丢失问题。

因此有了下面提高并发性能的组合拳

  • MVCC + 悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突
  • MVCC + 乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突

6.MVCC的实现原理?

它的实现原理主要是版本链undo日志Read View来实现的

6.1版本链

我们数据库中的每行数据,除了我们肉眼看见的数据,还有几个隐藏字段,得开天眼才能看到。分别是trx_iddb_roll_pointer

  • trx_id

    6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID

  • roll_pointer(版本链关键)

    7byte,回滚指针,指向这条记录上一个版本(存储于rollback segment里)

在这里插入图片描述

如上图,trx_id是当前操作该记录的事务ID,而roll_pointer是一个回滚指针,用于配合undo日志,指向上一个旧版本

在这里插入图片描述

每次对数据库记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer属性(INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来串成一个链表,所以现在的情况就像下图一样:

对该记录每次更新后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id(trx_id),这个信息很重要,在根据ReadView判断版本可见性的时候会用到。

6.2 undo日志

Undo log 主要用于记录数据被修改之前的日志,在表信息修改之前先会把数据拷贝到undo log里。

事务进行回滚时可以通过undo log 里的日志进行数据还原

Undo log 的用途

  • 保证事务进行rollback时的原子性和一致性,当事务进行回滚的时候可以用undo log的数据进行恢复
  • 用于MVCC快照读的数据,在MVCC多版本控制中,通过读取undo log历史版本数据可以实现不同事务版本号都拥有自己独立的快照数据版本

undo log主要分为两种:

  • insert undo log

    代表事务在insert新记录时产生的undo log , 只在事务回滚时需要,并且在事务提交后可以被立即丢弃

  • update undo log(主要)

    事务在进行update或delete时产生的undo log ; 不仅在事务回滚时需要,在快照读时也需要;

    所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

6.3 Read View(读视图)

事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照

记录并维护系统当前活跃事务的ID(trx_id)(没有commit,当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以越新的事务,ID值越大),是系统中当前不应该被本事务看到的其他事务id列表

Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

Read View几个属性

  • trx_ids: 当前系统活跃(未提交)事务版本号集合。
  • low_limit_id: 创建当前read view 时“当前系统最大事务版本号+1”。
  • up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号
  • creator_trx_id: 创建当前read view的事务版本号;

Read View可见性判断条件

在这里插入图片描述

(1)trx_id < up_limit_id || trx_id == creator_trx_id(显示)

如果数据事务ID(trx_id)小于read view中的最小活跃事务ID(up_limit_id),则可以肯定该数据是在当前事务启之前就已经存在了的,所以可以显示

或者数据的事务ID(trx_id)等于creator_trx_id ,那么说明这个数据就是当前事务自己生成的,自己生成的数据自己当然能看见,所以这种情况下此数据也是可以显示的。

(2)trx_id >= low_limit_id(不显示)

如果数据事务ID(trx_id)大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不显示

(3)如果trx_id < low_limit_id则进入下一个判断,判断trx_id是否在活跃事务(trx_ids)中

  • 不存在:则说明read view产生的时候事务已经commit了,这种情况数据则可以显示
  • 已存在:则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的。

通过上面的介绍我们已经大概了解了MVCC的基本原理,下面带大家实践下,以便更好的理解。

6.4 MVCC实践操作

比如有如下一条数据:

在这里插入图片描述

现在有个事务id是60的执行的这条记录的修改语句

在这里插入图片描述

此时在undo日志中就存在版本链

在这里插入图片描述

ReadView中主要就是有个列表(trx_ids)来存储我们系统中当前活跃着的读写事务,也就是begin了还未提交的事务。通过这个列表来判断记录的某个版本是否对当前事务可见。假设当前列表里的事务id为[80,100]。

此时有一个事务id为100的事务,修改了name,使得的name等于小明2,但是事务还没提交。则此时的版本链是

在这里插入图片描述

那此时另一个事务发起了select 语句要查询id为1的记录,那此时生成的ReadView 列表只有[100]。那就去版本链去找了,首先肯定找最近的一条,发现trx_id是100,也就是name为小明2的那条记录,发现在列表内(trx_ids),所以不能访问。

这时候就通过指针继续找下一条,name为小明1的记录,发现trx_id是60,小于列表中的最小id,所以可以访问,直接访问结果为小明1。

那这时候我们把事务id为100的事务提交了,并且新建了一个事务id为110也修改id为1的记录,并且不提交事务

在这里插入图片描述

这时候版本链就是

在这里插入图片描述

这时候之前那个select事务又执行了一次查询,要查询id为1的记录。

这个时候关键的地方来了

如果你是已提交读隔离级别,这时候你会重新一个ReadView,那你的活动事务列表(trx_ids)中的值就变了,变成了[110]。

按照上面的说法,由于[110]在活动事务列表(trx_ids)中,所以不能访问,这时候就通过指针继续找下一条,发现trx_id是100,name是小明2。

如果你是可重复读隔离级别,这时候你的ReadView还是第一次select时候生成的ReadView,也就是列表的值还是[100]。所以select的结果是小明1。所以第二次select结果和第一次一样,所以叫可重复读!

也就是说已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。

三、总结

本章首先介绍了快照读,然后介绍了MVCC和快照读的关系,最后详细分析了MVCC的实现原理并且通过一个实际应用使大家更加清晰的理解MVCC的具体实现。

最后引用我很佩服的一个人经常说的话:你知道的越多,你不知道的越多!

文章参考:

https://zhuanlan.zhihu.com/p/133480167

https://juejin.cn/post/6871046354018238472


http://chatgpt.dhexx.cn/article/dpUhu5lD.shtml

相关文章

MVCC 机制的原理及实现

什么是 MVCC MVCC (Multiversion Concurrency Control) 中文全程叫多版本并发控制&#xff0c;是现代数据库&#xff08;包括 MySQL、Oracle、PostgreSQL 等&#xff09;引擎实现中常用的处理读写冲突的手段&#xff0c;目的在于提高数据库高并发场景下的吞吐性能。 如此一来…

深入浅出:MVCC详解

什么是MVCC&#xff1a; MVCC(Multi Version Concurrency Control的简称)&#xff0c;代表多版本并发控制。与MVCC相对的&#xff0c;是基于锁的并发控制&#xff0c;Lock-Based Concurrency Control)。 MVCC最大的优势&#xff1a;读不加锁&#xff0c;读写不冲突。在读多写少…

什么是MVCC?MVCC解决了什么问题?MVCC的实现原理?

1.什么是MVCC&#xff1f; MVCC全称是【Multi-Version ConCurrency Control】&#xff0c;即多版本控制协议。 多版本控制&#xff08;Multiversion Concurrency Control&#xff09;: 指的是一种提高并发的技术。最早的数据库系统&#xff0c;只有读读之间可以并发&#xff…

MVCC详解,深入浅出简单易懂

一、什么是MVCC&#xff1f; mvcc&#xff0c;也就是多版本并发控制&#xff0c;是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。 数据库并发有以下几种场景&#xff1a; 读-读&#xff1a;不存在任何问题。读-写&#xff1a;有线程安全问题&#xff0c;可能出…

【MySQL笔记】正确的理解MySQL的MVCC及实现原理

MVCC多版本并发控制 如果觉得对你有帮助&#xff0c;能否点个赞或关个注&#xff0c;以示鼓励笔者呢&#xff1f;&#xff01;博客目录 | 先点这里 &#xff01;首先声明&#xff0c;MySQL 的测试环境是 5.7 前提概要 什么是 MVCC什么是当前读和快照读&#xff1f;当前读&…

Oracle自定义函数

使用Navicat的话&#xff0c;可以点击函数&#xff0c;新建函数&#xff0c;根据引导完成一个函数的基本搭建。 语法和Java类似&#xff0c;其中对于变量赋值要使用 : 进行赋值。 具体语法可以参考一下 Oracle 自定义函数语法与实例_桑汤奈伊伏的博客-CSDN博客_oracle 自定义函…

Oracle 创建函数

Oracle创建函数是通过PL/SQL自定义编写的&#xff0c;通过关键字function按照自己的需求把复杂的业务逻辑封装进PL/SQL函数中&#xff0c;函数提供一个返回值&#xff0c;返回给使用者。这样使用者就不需要去理解业务逻辑&#xff0c;把PL/SQL函数中的业务逻辑交给专门的开发人…

Oracle函数的使用

在进行select查询的时候&#xff0c;可以为列指定函数&#xff0c;函数是sql语句中的一个非常有用的特性&#xff0c;oracle内置了用于处理字符&#xff0c;数字&#xff0c;日期及转换的各种函数&#xff0c;使用函数能够执行数据计算&#xff0c;修改列数据的显示&#xff0c…

Oracle函数【详细 包括举例】

概述 Oracle SQL 提供了用于执行特定操作的专用函数。这些函数大大增强了 SQL 语言的功能。函数可以接受零个或者多个输入参数&#xff0c;并返回一个输出结果。 Oracle 数据库中主要使用两种类型的函数&#xff1a; 1. 单行函数&#xff1a;对每一个函数应用在表的记录中时&a…

Oracle 函数编写

CREATE OR REPLACE FUNCTION f_homestay_count (wkt_poly CLOB ) RETURN NUMBER IS result NUMBER ; BEGINSELECTCOUNT (*) INTO resultFROMHOMESTAY_BASIC TWHEREsdo_anyinteract (T .geom_point,sdo_geometry (wkt_poly, 4326)) TRUE; RETURN (result) ;END ;因为之前都是…

Oracle 自定义函数

语法结构 CREATE [OR REPLACE] FUNCTION 定义的函数名称(参数名1 参数类型,参数名2 参数类型, ...) RETURN 返回值类型 AS/IS 返回值形参 形参类型实例化 BEGIN 方法体 &#xff08;其中用到if判断的话&#xff0c;每一个if对应一个end if&#xff0c;出现几次if就会有几个end…

oracle常用函数

1.sign sign函数是根据给的数为正数&#xff0c;就返回1,0返回0&#xff0c;负数返回-1。需要注意sign&#xff08;这个括号里面只能是个字段&#xff09;&#xff0c;在括号中写个子查询直接就报错了&#xff01; 2.decode 用法&#xff1a; decode(条件,值1,返回值1,值2,返回…

Oracle(四)Oracle 函数

目录 函数介绍Oracle字符型函数Oracle日期型函数系统日期、时间函数&#xff1a;数据库时区函数&#xff1a;给日期加上指定的月份函数&#xff1a;月份最后一天函数:指定日期后一周的日期函数:返回指定日期中特定部分的函数&#xff1a;返回两个日期间的月份数&#xff1a;日期…

oracle函数instr函数

1 instr函数的概念 在Oracle中可以使用instr函数对某个字符串进行判断&#xff0c;判断其是否含有指定的字符。在一个字符串中查找指定的字符,返回被查找到的指定 的字符的位置。 2 语法 instr(sourceString,destString,start,appearPosition) instr&#xff08;‘源字符串’,‘…

Oracle常用函数【建议收藏】

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;目前从事DBA及程序编程 &#xff08;Web\java\Python&#xff09;工作&#xff0c;主要服务于生产制造 现拥有 Oracle 11g OCP/OCM、 Mysql、Oceanbase&#xff08;OBCA&#xff09;认证 分布式TBase\TDSQL数据库、国…

Oracle常用函数大全

说明&#xff1a;新文章地址转为 Oracle数据库函数大全_長安社-王于铭.YuMing的博客-CSDN博客https://hevnchin.blog.csdn.net/article/details/132054755 MySQL数据库系统函数大全_長安社-王于铭.YuMing的博客-CSDN博客year&#xff1a;年份、month&#xff1a;月份、day&am…

Oracle中的函数(详细!!!)

文章目录 前言一、SQL中的函数两种SQL函数单行函数单行函数的分类1. 字符型函数LOWER函数UPPER函数INITCAP函数CONCAT函数SUBSTR函数INSTR函数LPAD|RPAD函数REPLACE函数 2. 数字函数ROUND函数TRUNC函数MOD函数 3. 使用日期查看系统时间根据时间查询信息日期的运算MONTHS_BETWEE…

OpenDaylight通过netconf对接netopeer2

目的 利用OpenDaylight(client)的南向接口netconf对接netopeer2(server) 搭建netopeer2 启动Ubuntu20的docker&#xff0c;将内部830端口映射为主机的22830端口 adminubuntu20:~$ docker run -d --name netopeer2_server --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro…

ORAN专题系列-16:5G O-RAN FrontHaul前传接口的网络配置管理协议netconf

前言 前传接口&#xff08;FrontHual&#xff09;是传统的BBU与RU之间的接口&#xff0c;在O-RAN之前&#xff0c;前传接口虽然定义了物理连接的CPRI接口规范标准&#xff0c;但CPRI之上承载的M plane的配置管理数据格式&#xff0c;却是设备厂家私有的。 有基于TCP的、有基于…

实验三:Netconf 接口配置实验(基于Schema API)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Netconf简介1.基本网络架构2.协议框架3.报文格式4.会话建立过程 二、实验步骤1.设备预配2.运维代码编写3.实验结果 前言 云时代对网络的关键诉求之一是网络…