MVCC及其原理

1. MVCC概述及其原理

多版本并发控制(MVCC,Multi-Version Concurrency Control)是一种数据库管理技术,用于提高数据库系统在多用户环境中的并发性能,同时保证事务的隔离性,避免了不必要的锁定。MVCC允许在不同的事务中读取数据的早期版本,从而使读操作不会阻塞写操作,反之亦然。这种机制在很多数据库系统中都有实现,包括PostgreSQL和MySQL的InnoDB存储引擎。

在这里插入图片描述

MVCC原理

MVCC的核心思想是为数据库中的每一行数据保持不同版本的记录。这意味着当用户对数据库进行写操作(如更新或删除)时,系统会创建一行数据的新版本,而不是直接在原有数据上修改。每个版本的数据都有一个时间戳(或其他形式的版本标识),标识它被创建或修改的时间点。

当一个事务请求读取数据时,MVCC系统会返回该事务开始时刻可见的数据版本。具体来说,系统会根据以下规则来确定哪个版本的数据对当前事务是“可见”的:

  1. 创建版本号:每个数据版本在创建时都会被赋予一个唯一的版本号,这通常是事务的ID。这个版本号标识了数据被创建或修改的逻辑时间点。

  2. 删除版本号:当数据被另一个事务更新或删除时,原有版本的数据会被赋予一个删除版本号,也是事务的ID。这个版本号标识了数据停止被可见的逻辑时间点。

  3. 版本可见性规则:给定一个事务,如果某个数据版本的创建版本号小于或等于该事务的ID,且该数据版本没有被删除(即没有删除版本号)或其删除版本号大于该事务的ID,那么这个数据版本对该事务是可见的。

在这里插入图片描述

MVCC的优势

  • 提高并发性:读操作不会阻塞写操作,写操作也不会阻塞读操作,这大大提高了数据库的并发性能。
  • 减少锁的需求:由于读操作可以访问数据的早期版本,因此减少了对读写锁的需求,进一步提高并发性。
  • 事务隔离级别的支持:MVCC可以非常灵活地支持不同的事务隔离级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。

MVCC的实现

不同的数据库系统实现MVCC的具体机制可能有所不同,但基本原理相似。以MySQL的InnoDB存储引擎为例,它使用以下机制来实现MVCC:

  • Undo日志:当数据被修改时,原始数据会被存储在Undo日志中。这允许系统在需要时构造出数据的早期版本。
  • Read View:当事务开始时,InnoDB会为该事务创建一个“读视图”,决定哪些版本的数据对该事务可见。
  • 隐藏的系统列:InnoDB在每行数据中存储两个隐藏的系统列,记录了行的创建版本和删除版本,用于支持MVCC。

通过这种机制,MVCC能够在保证事务隔离性的同时,提高数据库的并发访问性能。

2. MySQL中的MVCC

在MySQL中,多版本并发控制(MVCC)主要通过InnoDB存储引擎实现。InnoDB使用一系列内部机制来支持MVCC,允许数据库在保持高并发的同时,确保数据的一致性和事务的隔离级别。这里是InnoDB实现MVCC的几个关键组成部分:

1. 隐藏列

InnoDB对每一行数据添加了三个隐藏的列来支持MVCC:

  • DB_TRX_ID:每当一行数据被修改时,InnoDB都会在这个隐藏列中存储修改该行的事务ID。
  • DB_ROLL_PTR:这个指针指向undo log记录,如果这行数据被多次修改,这些undo log记录形成一个链表。通过这个链表,InnoDB可以找到某个特定版本的行数据。
  • DB_ROW_ID:如果表没有定义主键,InnoDB会使用这个隐藏的行ID作为主键。

2. Undo日志

当事务更新数据时,InnoDB会将原始数据的副本存储在undo log中。这允许InnoDB在需要时回滚事务或重建旧的数据版本。对于MVCC来说,undo log使得读取事务能够看到事务开始之前的数据状态,即使这些数据后来被其他事务修改了。

3. Read View

当事务读取数据时,InnoDB会为该事务创建一个read view,这个视图定义了事务可以“看到”哪些行的版本。read view基于以下几个列表来判断数据行的可见性:

  • 活跃事务列表:在read view创建时,系统中所有活跃事务的ID列表。任何具有更高事务ID的数据修改都对当前事务不可见。
  • 上一个事务ID:创建read view时的最大事务ID。这帮助确定哪些数据版本是由尚未提交的事务创建的,因此对当前事务不可见。

4. 数据行的多个版本

通过以上机制,InnoDB能够为每个事务维护数据行的多个版本。当事务需要读取数据时,InnoDB会使用read view来决定哪个版本的数据对该事务是可见的。这取决于数据版本的创建事务ID与read view中活跃事务列表的比较。

如何工作

  • 当事务A更新一行数据时,InnoDB会将该行的当前版本写入undo log,并更新该行的DB_TRX_ID。
  • 如果此时事务B要读取相同的数据,InnoDB会检查事务B的read view。如果事务A的ID不在事务B的活跃事务列表中,这意味着事务A在事务B开始之前就已经提交,因此事务B可以看到事务A对该行所做的更改。
  • 如果事务A还未提交,事务B将看不到这些更改。InnoDB会使用undo log来为事务B提供该行数据的一个早期版本。

通过这种方式,InnoDB的MVCC机制支持了读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)等不同的事务隔离级别,并且允许并发事务高效安全地访问数据库,而无需对读操作加锁。

3. 例子

让我们通过一个简单的例子来说明InnoDB是如何实现MVCC的。假设我们有一个简单的银行账户表accounts,其中包含两列:account_id(账户ID)和balance(余额)。现在,我们有两个事务同时操作这个表:事务A(Tx A)要更新一个账户的余额,而事务B(Tx B)想要读取一些账户的余额信息。

初始状态

  • accounts表中有一条记录:account_id = 1, balance = 100

事务A的操作

  1. 开始事务A:事务A开始,并打算将account_id = 1的账户余额更新为200。
  2. 更新操作:InnoDB在更新余额之前,会将这行数据当前的版本(balance = 100)存储到undo log中,并将这行数据的DB_TRX_ID更新为事务A的事务ID。
  3. 余额更新account_id = 1的账户余额现在变为200。

事务B的操作

  1. 开始事务B:事务B开始,想要读取所有账户的余额信息。
  2. 构建Read View:为事务B创建一个read view,这个read view包含了事务开始时系统中所有未完成的事务ID。由于事务A尚未提交,它的ID也在这个列表中。

读取操作

  1. 事务B读取account_id = 1的余额:当事务B尝试读取这个账户的余额时,InnoDB检查这行数据的DB_TRX_ID
  2. 使用Read View判断数据版本的可见性:由于事务A的ID在事务B的read view活跃事务列表中,这意味着事务B不能看到事务A所做的更改。
  3. 通过Undo Log访问旧数据:InnoDB使用undo log中的信息,提供给事务B这行数据的旧版本(balance = 100),即使当前表中的数据已经被更新为200。

事务提交

  1. 事务A提交:事务A完成更新操作后提交。此时,InnoDB会更新这行数据的DB_TRX_ID,表示这个版本的数据是由事务A创建的。
  2. 事务B读取操作之后:即使事务A已经提交,由于事务B的read view是在事务B开始时创建的,事务B仍然只能看到旧版本的数据。

结论

通过这个例子,我们可以看到InnoDB的MVCC是如何工作的:

  • Undo Log:保留数据的旧版本,使得即使数据被更新,旧的事务也可以看到更新前的数据。
  • Read View:定义了一个事务可以看到哪些数据版本,确保事务的隔离性。
  • DB_TRX_ID:标识了数据版本是由哪个事务创建的,用于决定数据的可见性。

这种机制允许事务B在事务A更新数据的同时读取数据,而不会看到未提交的更改,从而实现了非锁定读取和高并发性。

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

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

相关文章

详解JavaScript的函数

详解 JavaScript 的函数 函数的语法格式 创建函数/函数声明/函数定义 function 函数名(形参列表) { 函数体 return 返回值; // return 语句可省略 } 函数调用 函数名(实参列表) // 不考虑返回值 返回值 函数名(实参列表) // 考虑返回值 示例代码 //定义的没有参数列表&am…

鸿蒙Harmony应用开发—ArkTS声明式开发(触摸事件)

当手指在组件上按下、滑动、抬起时触发。 说明: 从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 onTouch onTouch(event: (event: TouchEvent) > void) 手指触摸动作触发该回调。 卡片能力: 从…

备战蓝桥杯Day22 - 计数排序

计数排序问题描述 对列表进行排序,已知列表中的数范围都在0-100之间。设计时间复杂度为O(n)的算法。 比如列表中有一串数字,2 5 3 1 6 3 2 1 ,需要将他们按照从小到大的次序排列,得到1 1 2 2 3 3 5 6 的结果。那么此时计数排序是…

2000-2021年300+地级市进出口总额数据

2000-2021年300地级市进出口总额数据 1、时间:2000-2021年 2、指标:进出口总额 3、单位:万美元 4、来源:城市年鉴、各省年鉴、城市公报、2021年为城市统计年鉴中进口额出口额加总之后换算成万美元,已尽最大可能进行…

机器学习:集成学习(Python)

一、Adaboost算法 1.1 Adaboost分类算法 adaboost_discrete_c.py import numpy as np import copy from ch4.decision_tree_C import DecisionTreeClassifierclass AdaBoostClassifier:"""adaboost分类算法:既可以做二分类、也可以做多分类&#…

编译链接实战(25)ThreadSanitizer检测线程安全

ThreadSanitizer(又称为TSan)是一个用于C/C的数据竞争检测器。在并发系统中,数据竞争是最常见且最难调试的错误类型之一。当两个线程并发访问同一个变量,并且至少有一个访问是写操作时,就会发生数据竞争。C11标准正式将…

javaweb请求与响应

前言 前面介绍了对应的服务器端的相关代码。这里开始学习服务器端与客户端的数据请求与响应 这里的仅仅是一个简单的调用,并没有经过servelert接口来进行调用,同前面的一样,我们介绍对应的本地服务器进行的部署项目。 代码 //属于简单的不…

【硬件相关】RDMA网络类别及基础介绍

文章目录 一、前言1、RDMA网络协议2、TCP/IP网络协议 二、RDMA类别1、IB2、RoCE3、iWARP 三、RDMA对比1、优缺点说明a、性能b、扩展性c、维护难度 2、总结说明 一、前言 roce-vs-infiniband-vs-tcp-ip RoCE、IB和TCP等网络的基本知识及差异对比 分布式存储常见网络协议有TCP/IP…

CSS 【详解】响应式布局(含 rem 详解)

响应式布局: 同一页面在不同的屏幕上有不同的布局,即一套代码自适应不同的屏幕。 为什么 rem 能用于实现响应式布局? px 绝对长度单位,不同客户端表现都相同,不具有响应式em 相对长度单位,相对于父元素的 f…

小白学视觉 | 详解遗传算法 GA(Python实现代码)

本文来源公众号“小白学视觉”,仅用于学术分享,侵权删,干货满满。 原文链接:详解遗传算法 GA(Python实现代码) 转自:机器之心 英文:www.analyticsvidhya.com/blog/2017/07/introduc…

40、网络编程/TCP和UDP通信模型练习20240229

一、使用TCP模型创建服务器和客户端完成简单通信。 服务器代码&#xff1a; #include<myhead.h> #define SER_IP "192.168.32.130" #define SER_PORT 8888 int main(int argc, const char *argv[]) {//1.创建监听的套接字int sfd-1;sfdsocket(AF_INET,SOCK_S…

Vue2:用node+express部署Vue项目

一、编译项目 命令 npm run build执行命令后&#xff0c;我们会在项目文件夹中看到如下生成的文件 二、部署Vue项目 接上一篇&#xff0c;nodeexpress编写轻量级服务 1、在demo中创建static文件夹 2、将dist目录中的文件放入static中 3、修改server.js文件 关键配置&…