代码编织梦想

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更新数据的同时读取数据,而不会看到未提交的更改,从而实现了非锁定读取和高并发性。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_54187478/article/details/136390338

jeecg项目部署-爱代码爱编程

说明:Jeecg是一款低代码开发平台,简单说是一款现成的项目,该项目集成了许多功能,我们可以在这个项目之上开发自己的业务代码。 本文介绍Jeecg项目的部署,包括后端jeecg-boot项目、前端vue3项目。前端项目在本

lv21 qt入门与基础控件 1-爱代码爱编程

1 QT简介 QT是挪威Trolltech开发的多平台C++图形用户界面应用程序框架 典型应用 2 工程搭建 2.1 新建ui工程  不要写中文路径   2.1 不勾选UI(主讲) 3 QT信号与槽机制  语法:Connect(A, SIGNLA(aaa()),  B, SLOT(bbb()));  

宏景ehr displayfiles 任意文件读取漏洞-爱代码爱编程

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 Ⅰ、漏洞描述 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。

mysql学习day23——索引优化与查询优化-爱代码爱编程

SQL查询优化的技术有很多,大方向可分为物理查询优化和逻辑查询优化 物理查询优化:通过索引和表连接方式等技术来进行优化; 逻辑查询优化:通过SQL等价变换提升查询效率 一、索引失效案例: 是否使用索引是由优化器决定,优化器基于cost开销而不是规则和语义。SQL语句是否使用索引跟数据库版本、数据量、数据选择度都有关系。 1.全值匹配我最爱 2.

goleveldb构建数据字典-爱代码爱编程

GoLevelDB 是一个开源的键值存储数据库,可以用于构建数据字典,下面是一些示例代码,展示了如何使用 GoLevelDB 来生成数据字典。 首先,你需要在 Go 中导入 GoLevelDB 包,并创建一个数据库实例。可以使用 leveldb.OpenFile 函数来创建或打开一个数据库文件。 package main import ( "fmt

mysql存储引擎及索引机制-爱代码爱编程

MySQL技术——存储引擎和索引机制 一、存储引擎概述二、常见存储引擎的区别三、索引机制四、索引的底层实现原理五、InnoDB主键和二级索引六、聚集索引和非聚集索引七、哈希索引八、InnoDB的自适应哈希索引九

数据库结构变更同步:增量 vs 差异-爱代码爱编程

嗨!大家好,我是波罗学。本文是 Golang 三方库推荐第三篇,主要是闲扯,为后面两个工具做铺垫。系列查看:Golang 三方库。 平时在开发项目时,常会需要改动数据库结构,比如说,有时候需要加个表,改个字段名,这是个

java毕业设计-爱代码爱编程

文章目录 前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1.开发说明2.需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、后台功能模块2.1管理员功能2.2用户功能2.3教练功能

mysql深入——23-爱代码爱编程

主机内存只有100G,现在对一个200G的大表进行扫描,会不会把数据库的内存用完。 对大表做全表扫描对Sever层的影响 假设现对一个200G的InnoDB表db1,做一个全表扫描,当然要把扫描结果保存到客户端。 InnoDB的数据时保存在主键索引上的,所以全表扫描实际上是扫描表t的主键索引,最后返回给客户端。 返回的结果集并不是完整的,因为MyS

mybatis-爱代码爱编程

MyBatis是一款优秀的持久层框架,用于简化JDBC开发 持久层: 负责将数据到保存到数据库的那一层代码 JavaEE三层架构:表现层、业务层、持久层 框架: 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型 在框架的基础之上构建软件编写更加高效、规范、通用、可扩展 1,模板  其中: (1)logback.xml

浅析能耗监测系统在大型数据中心的应用-爱代码爱编程

彭姝麟 Acrelpsl 1总体设计        大型数据中心能耗监测系统包含硬件和软件两大部分,其硬件组成主要包括监控服务器、主机设备、网络设备、环境参数传感器、通风模块等,总体采集逻辑采用三级监控体系。一级为主机设备,作为系统的应用层,为用户提供人机接口;二级是主机状态,负责采集主机性能参数或对主机进行远程控制;三级是传感器以及能耗装置,根据传输

hgame题解(第二星期)-爱代码爱编程

Hgame题解(第二星期) Web Select More Courses 打开靶机发现是一个登陆页面,根据题目提示下载弱密码字典,通过BP爆破获得用户密码为qwert123 登陆后进入下一个页面,由于学分已满无