多版本并发控制 MVCC

介绍多版本并发控制

多版本并发控制技术(Multiversion Concurrency Control,MVCC)

技术是为了解决问题而生的,通过 MVCC 我们可以解决以下几个问题:

  1. 读写之间阻塞的问题:通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。
  2. 降低了死锁的概率:这是因为 MVCC 没有使用锁,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
  3. 解决一致性读的问题:一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交更新的结果。

MVCC 的思想

MVCC 是通过数据行的历史版本来实现数据库的并发控制。

简单来说 MVCC 的思想就是保存数据的历史版本。这样一个事务进行查询操作时,就可以通过比较版本号来判断哪个较新的版本对当前事务可见。

InnoDB 对 MVCC 的实现

MVCC 没有正式的标准,所以在不同的 DBMS 中,MVCC 的实现方式可能是不同的。

InnoDB 对 MVCC 的实现主要是通过 版本链 + ReadView 结构完成。

版本链存储记录的多个版本

先介绍聚簇索引记录的隐藏列,再介绍 Undo Log 版本链

对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含 3 个隐藏列

  1. db_row_id:隐藏的行 ID。在没有自定义主键也没有 Unique 键的情况下,会使用该隐藏列作为主键。
  2. db_trx_id:操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。
  3. db_roll_ptr:回滚指针,也就是指向这个记录的 Undo Log 信息。Undo Log 中存储了回滚需要的数据。

事务ID
事务执行过程中,只有在第一次真正修改记录时(比如进行 insert、delete、update 操作),才会被分配一个唯一的、单调递增的事务 ID,如果没有修改记录操作,按照一定的策略分配一个比较大的事务 ID,减少分配事务 ID 的锁竞争。每当事务向数据库写入新内容时, 所写的数据都会被标记操作所属的事务的事务ID。

在 InnoDB 存储引擎中,版本链由数据行的 Undo Log 组成。

每次对数据行进行修改,都会将旧值记录到 Undo Log,算是该数据行的一个旧版本。

Undo Log 有两个重要的属性:db_roll_ptr、db_trx_id

  • Undo Log 也有一个 db_roll_ptr 属性(insert 操作对应的 Undo Log 没有 db_roll_ptr 属性,因为 insert 操作对应的数据行没有更早的版本),Undo Log 的 db_roll_ptr 属性指向上一次操作的 Undo Log,所有的版本被 db_roll_ptr 属性连接形成一个链表。该链表即版本链,版本链的头节点就是数据行的最新值。
  • Undo Log 还包含生成该版本时,对应的事务 ID,用于判断当前版本的数据对事务的可见性。

版本链如下图所示。这样如果我们想要查找历史快照,就可以通过遍历回滚指针的方式进行查找。

多版本并发控制 MVCC

ReadView 判断版本链中的哪个较新的版本对当前事务是可见的

ReadView 用来判断版本链中的哪个较新的版本对当前事务是可见的。

ReadView 中主要包含 4 个比较重要的属性:

  • m_ids:表示在生成 ReadView 时,当前系统中所有活跃的读写事务的 ID 集合(列表)
  • min_transaction_id:表示在生成 ReadView 时,m_ids 中的最小值
  • max_transaction_id:表示在生成 ReadView 时,系统应该分配给下一个事务的 ID 值
  • creator_transaction_id:表示生成该 ReadView 的事务的 ID

有了这个 ReadView,这样在访问某条记录时,就可以用 ReadView 来判断版本链中的哪个较新的版本对当前事务是可见的。

  • 如果被访问版本的 transaction_id 属性值与 ReadView 中的 creator_trx_id 值相同,表明当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的 transaction_id 属性值 小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交了,所以该版本可以被当前事务访问。
  • 如果被访问版本的 transaction_id 属性值 大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的 transaction_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 transaction_id 属性值是不是在 m_ids 列表中:
  • 如果在,表明生成 ReadView 时,被访问版本的事务还是活跃的,所以该版本不可以被当前事务访问
  • 如果不在,表明生成 ReadView 时,被访问版本的事务已经被提交了,所以该版本可以被当前事务访问

如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断

可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对当前事务完全不可见,查询结果就不包含该记录。

ReadView 的生成时机

MVCC 可以防止脏读,也可以防止不可重复读。

防止脏读 和 防止不可重复读 实现的不同之处就在:ReadView 的生成时机不同

  • 防止脏读:每次读取数据前,都生成一个 ReadView
  • 防止不可重复读:在当前事务第一次读取数据时,生成一个 ReadView,之后的查询操作都重复使用这个 ReadView

对于隔离级别为 读未提交 的事务来说,直接读取记录的最新版本即可。

对于隔离级别为 串行化 的事务来说,InnoDB 存储引擎使用加锁的方式来访问记录。

对于隔离级别为 读已提交 和 可重复读 的事务来说,都必须保证只能读到已经提交的事务修改的数据,不能读到未提交的事务修改的数据。

参考资料

MySQL 是怎样运行的:从根儿上理解 MySQL – 小孩子4919 – 掘金课程 (juejin.cn)

Original: https://www.cnblogs.com/feiyu2/p/mvcc.html
Author: 真正的飞鱼
Title: 多版本并发控制 MVCC

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/598890/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL高可用安装

    MySQL HA部署 环境准备 创建本地yum源 确认关闭 SELinux 防火墙设置 MySQL安装 使用 root 用户操作创建相关的用户组和用户 上传/解压介质 设置自启动 …

    数据库 2023年5月24日
    0102
  • Dubbo源码(六)-服务路由

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 今天,来聊点短的,服务路由 Router,本文讲的是路由的调用路径,不讲路由…

    数据库 2023年6月11日
    071
  • 数据库的常用命令

    1. 数据操作类语句: SELECT:从数据库表&#x4E2D…

    数据库 2023年5月24日
    0103
  • 关于pycharm打开时很卡,一直加载中的解决办法~

    相信很多刚开始使用pycharm不太熟练的小伙伴,每天一开机打开pycharm总是卡半天,不知道的还以为是电脑卡了或者啥问题的。 莫慌,其实并不是… 今天我们就来解决一…

    数据库 2023年6月14日
    0109
  • 8 int和Integer的区别

    数据类型 int是基本数据类型,Integer是int的包装类,属于引用类型 初始值 int的初始值为0,Integer的初始值为null 存储位置 int是直接存储在栈中的,In…

    数据库 2023年6月6日
    090
  • Linux中的grep,sed,find的使用方法

    提取指定文件到指定目录/文件 语法:tar xvf test.tar test1(只提取test.tar 包中的test1文件到当前路径) 语法:tar xvf test.tar …

    数据库 2023年6月16日
    075
  • html简单学习!

    博主学习html的随记 1.常用标签 1.基础标签 2.格式标签 3.表单 4.超文本标签 5.列表 6.表格 7.样式 8.特殊符号 9.内联框架(网页嵌套) 1.常用标签 1….

    数据库 2023年6月16日
    091
  • 数据库原理一—MySQL基本架构与索引

    MySQL基本架构 Server层包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现。存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持In…

    数据库 2023年6月6日
    076
  • Java学习-第一部分-第三阶段-第三节:MySQL基础

    零基础学MySQL 笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 一个问题 淘宝网,京东、微信,抖音都有各自的…

    数据库 2023年6月11日
    093
  • 第十二章 自定义类型转换器

    1.类型转换器 作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成对象中成员变量对应类型的数据,从而完成注入 2.自定义类型转换器 当Spring内部没有提供特定…

    数据库 2023年6月14日
    080
  • Maven项目添加外来jar包

    本例: 是为了读取Excel文件并把数据录入数据库,从数据库读写数据 写入Excel文件 用到的jar包:mysql—-poi Original: https://ww…

    数据库 2023年6月9日
    077
  • MySQL max() min() 函数取值错误

    今天日志出现异常,一步一步debug发现SQL语句返回值出错,进一步发现是max()函数返回出错。点击跳转解决办法,赶时间的朋友可以去获得答案。当然我还是希望大伙看看原由。 sel…

    数据库 2023年6月16日
    0175
  • Java 可重入锁的那些事(一)

    本文主要包含的内容:可重入锁(ReedtrantLock)、公平锁、非公平锁、可重入性、同步队列、CAS等概念的理解 显式锁🔒 上一篇文章提到的synchronized关键字为隐式…

    数据库 2023年6月6日
    0113
  • Mysql数据库存取原理及性能优化

    一、Mysql的系统架构图 二、Mysql存储引擎 Mysql中的数据是通过一定的方式存储在文件或者内存中的,任何方式都有不同的存储、查找和更新机制,这意味着选择不同的方式对于数据…

    数据库 2023年6月14日
    099
  • Linux磁盘分区和挂载

    磁盘分区和挂载 挂载概念 在linux操作系统中,挂载是一个非常重要的功能,使用非常频繁。 它指将一个设备(通常是存储设备)挂接到一个已存在的目录上。需要理解的是,linux操作系…

    数据库 2023年6月16日
    080
  • MYSQL–>事务

    事务是一组操作的集合,它是一个不可分割的工作单位。 事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,这些操作要么同时成功,要么同时失败 开启事务—->…

    数据库 2023年6月14日
    073
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球