MySQL中大对象的多版本并发控制详解

MySQL 8.0:InnoDB中大对象的MVCC

在本文中,我将解释MySQL InnoDB存储引擎中大对象(LOB)设计的多版本并发控制(MVCC) 。 MySQL 8.0有一个新功能,允许用户部分更新大型对象,包括JSON文档 。 使用此部分更新功能,当LOB部分更新时,MVCC对LOB的工作方式已发生变化。 对于正常更新(完整更新),MVCC将像以前的版本一样工作。 让我们看一下MVCC在不涉及部分更新时的工作原理,然后考虑对LOB进行部分更新的用例。

MVCC 常规更新

我使用术语常规更新来指代不是部分更新的更新。 我将通过一个例子解释MVCC如何用于常规更新大对象。 我将为此目的使用以下mtr(1)测试用例:

 create table t1 ( f1 int primary key , f2 longblob ) engine = innodb ;
 insert into t1 values ( 1 , repeat ( 'a' , 65536 ) ) ; 

 start transaction ;
 update t1 set f2 = repeat ( 'b' , 65536 ) where f1 = 1 ; 

 -- echo # Connection con1:
 -- 对于使用MySQL客户端的用户,可能需要通过另开一个终端窗口建立新链接, 下同。
 connect ( con1 , localhost , root , , ) ;
 -- echo # Must see the old value 'aaaaaaaaaa'
 select f1 , right ( f2 , 10 ) from t1 order by f1 ; 

 -- echo # Connection default:
 connection default ;
 disconnect con1 ;
 commit ; 

 drop table t1 ; 

为了理解下面的解释,仔细理解上述测试用例非常重要。

测试场景如下:

最初,表t1包含单个记录(R1)。
事务trx1将记录更新为新值。
当trx1仍处于活动状态时,另一个事务trx2正在读取记录。 它将读取旧值。

表t1仅包含一个记录(R1)。 但是trx1和trx2会看到两个不同的值。 该表实际上只包含最新值(trx1所见的值),而trx2看到的值或记录是从撤消日志记录中获得的。 让我们看下面的图片来更好地理解它。

初始状态:更新操作之前

下图显示了更新操作之前的情况。 撤消日志为空。 表的聚簇索引包含一行。 表中有一个LOB。 聚簇索引记录包含对LOB的引用。

最终状态:更新操作后

现在让我们看一下更新操作后的情况。

以下是一些重要的观察:

用户表空间中有两个LOB - 旧的LOB和新的LOB。 旧的LOB只能通过撤消日志访问。 聚集索引记录指向新LOB。
更新操作已创建包含更新向量的撤消日志记录。 此撤消日志记录指向旧LOB。
聚簇索引记录通过DB_ROLL_PTR系统列指向撤消日志记录。 此滚动指针指向撤消日志记录,该记录可用于构建聚簇索引记录的先前版本。
撤消记录不包含LOB本身。 而是它只包含对存储在用户表空间中的LOB的引用。
存储在撤消日志记录中的LOB引用与存储在聚簇索引记录中的LOB引用不同。

事务在连接1中采取的步骤如下:

事务查看R1并确定尚未提交修改聚簇索引记录的事务。 这意味着它无法读取该记录(因为默认隔离级别是REPEATABLE READ)。
它查看R1中的DB_ROLL_PTR并找到撤消日志记录。 使用撤消日志记录构建R1的先前版本。
它读取了这个构建的旧版R1。 请注意,此版本在聚簇索引记录中不可用。 但它使用撤消记录即时构建。
当R1指向新的LOB时,这个构造的旧版本的R1指向旧的LOB。 所以结果包含旧的LOB。

这是LOB的MVCC在不涉及部分更新时的工作方式。

MVCC部分更新

让我们看另一个例子,了解MVCC在部分更新的情况下是如何工作的。 我们需要另一个例子,因为目前仅通过函数json_set()和json_replace()支持JSON文档的部分更新。

 create table t2 ( f1 int primary key , j json ) engine = InnoDB ;
 set @ elem_a = concat ( '"' , repeat ( 'a' , 200 ) , '"' ) ;
 set @ elem_a_with_coma = concat ( @ elem_a , ',' ) ;
 set @ json_doc = concat ( "[" , repeat ( @ elem_a_with_coma , 300 ) , @ elem_a , "]" ) ; 

 insert into t2 ( f1 , j ) values ( 1 , @ json_doc ) ; 

 start transaction ;
 update t2 set j = json_set ( j , '$[200]' , repeat ( 'b' , 200 ) ) where f1 = 1 ; 

 -- echo # Connection con1:
 connect ( con1 , localhost , root , , ) ;
 -- echo # Must see the old value 'aaaaaaaaaa...'
 select json_extract ( j , '$[200]' ) from t2 ; 

 -- echo # Connection default:
 connection default ;
 disconnect con1 ;
 commit ; 

该场景与前面的示例相同。 只是longblob字段已更改为JSON文档。 加载的数据也略有不同,以符合JSON格式。

提示 :您可以在上述mtr测试用例(两者中)中添加语句set debug ='+ d,innodb_lob_print' ,以在服务器日志文件中打印LOB索引。 LOB索引将在插入后立即打印。 LOB索引将为您提供存储的LOB对象的结构。
在部分更新操作之前

完全或部分更新操作之前的初始条件是相同的,并且已经在上面给出。 但是在下图中,提供了一些附加信息。

让我们看看图中显示的其他信息:

存储在聚簇索引记录中的LOB引用现在包含LOB版本号v1。 在初始插入操作期间,将其设置为1,并在每次部分更新时递增。
每个LOB数据页面在LOB索引中都有一个条目。 每个条目都包含LOB版本信息。 每当修改一个LOB数据页时,它将被复制到具有新数据的新LOB数据页中,并且将创建具有递增的LOB版本号的新LOB索引条目。

附加信息是LOB版本号。 这在聚集索引记录中的LOB引用中以及LOB索引的每个条目中都可用。

部分更新操作后

下图说明了部分更新操作后的情况。

这里最重要的优化是用户表空间中仍然只有一个LOB。 仅更新需要修改的那些LOB数据页。 部分更新操作后的这个单个LOB包含旧版本和新版本的LOB。 图中LOB数据页面上的v1和v2标签说明了这一点。

另一个重要的观察是撤消日志和聚簇索引记录中的LOB引用指向同一个LOB。 但LOB引用包含不同的版本号。 撤消日志记录中的LOB引用包含v1(旧版本号),聚簇索引记录中的LOB引用包含新版本号v2。

LOB版本号的目的

如上所示,具有不同版本号的不同LOB引用指向相同的LOB。 单个LOB包含来自不同版本的部分。 LOB版本号用于获取各种LOB引用指向的正确版本。 在本节中,我们将了解如何完成此操作。

LOB索引包含组成LOB的LOB页面列表。 它包含LOB数据页的页码,每个LOB数据页包含的数据量以及版本号。 此列表的每个节点称为LOB索引条目。 每个LOB索引条目都包含旧版本的列表。 让我们看一个说明上述部分更新测试用例的结构的图。

最初,在完成部分更新之前,LOB索引总共包含4个条目。 四个条目的页码是5,6,7和8.没有LOB索引条目具有旧版本。 所有四个条目的版本号均为1。

部分更新完成后,我们注意到页码9已替换页码7,页码7现在被视为页码9的旧版本。页码9的版本号为2,并且页码7的版本号为1。

部分更新完成后,当通过版本号为1的LOB引用访问LOB时,将查看第5页的第一个索引条目。 它的版本号为1.如果索引条目中的版本号小于或等于 LOB引用中的版本号,则将读取该条目。 因此,将读取第5页。 然后将查看页码为6的索引条目。 它的版本号为1,因此将被读取。 然后将查看页码为9的索引条目。 它的版本号为2.但是lob引用的版本号为1.如果索引条目中的版本号大于LOB引用中的版本号,则不会读取该条目。 由于页码9的条目具有版本2,因此将查看其旧版本。 将检查页码为7的索引条目。 它的版本号为1,因此将被读取。 在此之后,将检查页码为8的索引条目。 它的版本号为1,因此也将被读取。 这是访问旧版LOB的方式。

部分更新完成后,当通过版本号为2的LOB引用访问LOB时,将查看第5页的第一个索引条目。 它的版本号为1.如果索引条目中的版本号小于或等于LOB引用中的版本号,则将读取该条目。 因此它将按顺序读取页码5,6,9,8。 由于版本号始终<= 2,因此无需使用旧版本访问页码7。

需要记住的一点是LOB在InnoDB中不是独立存在的。 它被视为聚簇索引记录的扩展。LOB对事务是否可见并不由LOB模块处理。 LOB模块只是处理聚簇索引记录。 如果事务访问LOB,则意味着它已经在聚簇索引记录中的DB_TRX_ID的帮助下确定它可以查看LOB(而不是LOB的特定版本)。 所以我们不担心LOB模块中的那个方面。 我们只专注于为给定的LOB版本号提供正确的内容。

结论

在本文中,我们了解了如何在InnoDB中为大对象完成MVCC。 当对LOB进行部分更新时,多个LOB引用可以指向同一个LOB。 但他们将拥有不同的版本号。 使用这些LOB版本号,可以访问正确的LOB内容。

希望您发现此信息有用。

谢谢你使用MySQL!

注释:

(1) Mtr即Mini-transaction的缩写,字面意思小事物,相对逻辑事物而言,我们把它称作物理事物。属于Innodb存储引擎的底层模块。主要用于锁和日志信息。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 深入mysql并发插入优化详解

    使用storm处理日志的时候,经常会遇到并发插入mysql的效率问题,到网上查了些资料,做一下笔记将表的引擎改为 myisam, 修改 my.cnf 的concurrent_insert=2,concurrent_insert 可以设的值有 0 1 2 ,2 是完全支持并发插入 1) concurrent _insert =0 ,无论MyISAM的表数据文件中间是否存在因为删除而留下俄空闲空间,都不允许concurrent insert. 2)concurrent_insert = 1,是当My

  • 分析MySQL并发下的问题及解决方法

    1.背景 对于数据库系统来说在多用户并发条件下提高并发性的同时又要保证数据的一致性一直是数据库系统追求的目标,既要满足大量并发访问的需求又必须保证在此条件下数据的安全,为了满足这一目标大多数数据库通过锁和事务机制来实现,MySQL数据库也不例外.尽管如此我们仍然会在业务开发过程中遇到各种各样的疑难问题,本文将以案例的方式演示常见的并发问题并分析解决思路. 2.表锁导致的慢查询的问题 首先我们看一个简单案例,根据ID查询一条用户信息: mysql> select * from user where

  • 并发环境下mysql插入检查方案

    业务背景: 基本业务场景是这样的,请求数据(车辆vin信息)进入到接口中,需要先判断其在数据库中的状态,如果库中不存在该vin,或者该vin状态位为"1(已完成)",则执行一些检查操作后,将数据插入到数据库中,此时新增vin状态为0,调用人工处理接口,十分钟后返回结果,将状态置为1.如果其状态位为"0(正在处理)"则驳回操作,返回提示信息. 在单线程环境下,这样的业务没有问题,然而当并发访问接口时,会出现同时进入两条vin相同的请求AB,正常情况应该插入一条A,驳回

  • MySQL中大对象的多版本并发控制详解

    MySQL 8.0:InnoDB中大对象的MVCC 在本文中,我将解释MySQL InnoDB存储引擎中大对象(LOB)设计的多版本并发控制(MVCC) . MySQL 8.0有一个新功能,允许用户部分更新大型对象,包括JSON文档 . 使用此部分更新功能,当LOB部分更新时,MVCC对LOB的工作方式已发生变化. 对于正常更新(完整更新),MVCC将像以前的版本一样工作. 让我们看一下MVCC在不涉及部分更新时的工作原理,然后考虑对LOB进行部分更新的用例. MVCC 常规更新 我使用术语常规

  • MySQL数据类型中DECIMAL的用法实例详解

    MySQL数据类型中DECIMAL的用法实例详解 在MySQL数据类型中,例如INT,FLOAT,DOUBLE,CHAR,DECIMAL等,它们都有各自的作用,下面我们就主要来介绍一下MySQL数据类型中的DECIMAL类型的作用和用法. 一般赋予浮点列的值被四舍五入到这个列所指定的十进制数.如果在一个FLOAT(8, 1)的列中存储1. 2 3 4 5 6,则结果为1. 2.如果将相同的值存入FLOAT(8, 4) 的列中,则结果为1. 2 3 4 6. 这表示应该定义具有足够位数的浮点列以便

  • JDBC连接mysql处理中文时乱码解决办法详解

    JDBC连接mysql处理中文时乱码解决办法详解 近日,整合的项目需要跟一个比较老版本的mysql服务器连接,使用navicat查看,发现此mysql服务器貌似没有设置默认编码,而且从操作此mysql的部分php文件看,应该是使用的gb2312的编码,但是,直接使用jdbc操作,从库中读取出来的中文全都是乱码. 一开始,使用类似entity.setDepartName(new String(rs.getString("hg").getBytes("gbk"), &q

  • MySQL 清除表空间碎片的实例详解

    MySQL 清除表空间碎片的实例详解 碎片产生的原因 (1)表的存储会出现碎片化,每当删除了一行内容,该段空间就会变为空白.被留空,而在一段时间内的大量删除操作,会使这种留空的空间变得比存储列表内容所使用的空间更大: (2)当执行插入操作时,MySQL会尝试使用空白空间,但如果某个空白空间一直没有被大小合适的数据占用,仍然无法将其彻底占用,就形成了碎片: (3)当MySQL对数据进行扫描时,它扫描的对象实际是列表的容量需求上限,也就是数据被写入的区域中处于峰值位置的部分: 例如: 一个表有1万行

  • C++利用MySQL API连接和操作数据库实例详解

    1.C++连接和操作MySQL的方式 系列文章: MySQL 设计和命令行模式下建立详解 C++利用MySQL API连接和操作数据库实例详解 在Windows平台,我们可以使用ADO.ODBC或者MySQL API进行连接和操作.ADO (ActiveX Data Objects,ActiveX数据对象)是Microsoft提出的一个用于存取数据源的COM组件.它提供了程序语言和统一数据访问方式OLE DB的一个中间层,也就是Microsoft提出的应用程序接口(API)用以实现访问关系或非关

  • LInux下安装MySQL5.6 X64版本步骤详解

    环境: 1.CentOS6.5 X64 2.mysql-5.6.34-linux-glibc2.5-x86_64.tar.gz 3.下载地址:http://dev.mysql.com/downloads/mysql/ 安装步骤 1.再安装包的存放目录下执行命令解压文件 tar -zxvf mysql-5.6.34-linux-glibc2.5-x86_64.tar.gz -C /opt/ 2.删除安装包,重命名解压后的文件 rm -rf mysql-5.6.34-linux-glibc2.5-x

  • MySQL教程数据定义语言DDL示例详解

    目录 1.SQL语言的基本功能介绍 2.数据定义语言的用途 3.数据库的创建和销毁 4.数据库表的操作(所有演示都以student表为例) 1)创建表 2)修改表 3)销毁表 如果你是刚刚学习MySQL的小白,在你看这篇文章之前,请先看看下面这些文章.有些知识你可能掌握起来有点困难,但请相信我,按照我提供的这个学习流程,反复去看,肯定可以看明白的,这样就不至于到了最后某些知识不懂却不知道从哪里下手去查. <MySQL详细安装教程> <MySQL完整卸载教程> <这点基础都不懂

  • MySQL数据库完全备份与增量备份详解

    目录 定义 完全备份与恢复演示 定义 完全备份就是将数据库中的数据及所有对象全部备份. 由于 MySQL 服务器中的数据文件是基于磁盘的文本文件,所以完全备份就是复制数据库文件,是最简单也是最快速的方式. 但 MySQL 服务器的数据文件在服务器运行期间,总是处于打开状态,为实现真正的完全备份,需要先停止 MySQL 数据库服务器. 为了保障数据的完整性,在停止 MySQL 服务器之前,需要先执行 flush tables 语句将所有数据写入到数据文件中.对于该方法同学们只需了解,因为将生产环境

  • SQLServer数据库从高版本降级到低版本实例详解

    SQLServer数据库从高版本降级到低版本实例详解 由于目前还广泛使用着SQLServer2000,很多公司又想使用新的SQLServer,从而直接[分离/附加]或者[备份/还原]数据库,在不同版本之间存放.往往就会遇到版本不兼容的问题.前几天遇到了从我本机2008R2上备份的一个数据库还原到2008上面时报错: 从运行版本10.50.2500(2008R2是10.50)和10.00.1600(2008是10.00)中可以看出这个版本不兼容问题,大部分情况下,从低版本升级到高版本,只要不是跨度

  • js基础之DOM中document对象的常用属性方法详解

    -----引入 每个载入浏览器的 HTML 文档都会成为 Document 对象. Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问. 属性 1  document.anchors  返回对文档中所有 Anchor 对象的引用.还有document.links/document.forms/document.images等 2  document.URL       返回当前文档的url 3  document.title       返回当前文档的标题 4  do

随机推荐