当前位置: 移动技术网 > IT编程>数据库>Mysql > MySQL中大对象的多版本并发控制详解

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

2018年11月05日  | 移动技术网IT编程  | 我要评论

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存储引擎的底层模块。主要用于锁和日志信息。

总结

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网