数据库开发者社区 > 正文

MySQL8.0.12 · 引擎特性 · LOB Partial Update优化

简介: 在之前,笔者介绍过InnoDB对于lob列的更新优化,即允许对lob类型的列数据进行部分更新。由于undo log page本身的限制(例如无法存储过长的数据),对于大列更新,旧版本被留在数据文件中,在MVCC读时,直接从中读旧版本即可。
+关注继续查看

在之前,笔者介绍过InnoDB对于lob列的更新优化,即允许对lob类型的列数据进行部分更新。由于undo log page本身的限制(例如无法存储过长的数据),对于大列更新,旧版本被留在数据文件中,在MVCC读时,直接从中读旧版本即可。然而对于超长lob列数据,标记删除旧版本再插入完整新数据的开销太大了,尤其是对于json列,通常我们只需要修改其中极少部分的数据。 为了解决这个问题,InnoDB在8.0版本中实现了partial update的概念,将更新的范围缩小到page单位,并对lob Page辅助以索引,每个索引项可以维持一个lob page的多个版本(For MVCC)

WL#11328认为可以对部分更新操作做进一步的优化, 举个简单的例子,一个Page内可能只修改了几十个字节,却需要创建一个新的page,这依然会产生不少的开销,因此在MySQL8.0.12中,对这部分逻辑进行了进一步的优化:当更新少于某个阈值时,采用Undo来记录老的lob数据修改。在需要读数据时,将这部分修改apply到lob列中。根据官方博客中的测试,最多带来了接近三倍的TPS提升,还是相当理想的。

本文主要记录下涉及到的相关代码, 基于MySQL8.0.12。

update

计算更新的字节数

MySQL Server层实际上已经记录了Lob diff,对字段的修改产生的diff维护在Binary_diff_vector中,vector中每个元素类型为Binary_diff,代表对列上的一部分的修改。对一列的更新可能产生多个binary diff。

InnoDB据此信息,去定位到对应的lob数据,InnoDB当前hardcode了一个值LOB_SMALL_CHANGE_THRESHOLD,默认为100字节,当更新的字节数(upd_t::get_total_modified_bytes())小于这个阈值时,走新的逻辑,否则走之前的逻辑(产生一个新的lob page,并递增版本)

写undo

由于在undo中记录的是部分更新,而不是全部Lob数据,undo log的格式需要做一些改动(这意味着升级到8.0.12之后将无法降级到之前的版本), 主要如下:

  • 增加一个flag TRX_UNDO_MODIFY_BLOB, 表示Undo log支持 lob partial update。
  • 新扩展一个byte,用于未来使用
  • 将Binary diffz中存储的老数据(以及对应lob index entry信息)记录入undo log

一个典型的Undo log包含(取自官方博客):

image

ref: trx_undo_page_report_modify

新的修改在记录update vector这里做了扩展,下图取自官方博客:

image

入口函数: trx_undo_report_blob_update

  • 检查所有binary diff的长度是否超过100字节,如果超过了,表明不是small update,则数组长度设置为0,并返回
  • 对于small update,每个binary_diff占用一项,前面提到过一个Lob更新可能包含多个binary diff, 因此每个binary diff都顺序记录到undo log中
  • 而对于一个binary diff,其记录的是数据的修改,但落到数据页上可能横跨两个lob page,这就涉及到最多两个lob index entry,其事务信息同样需要记录下来(lob::get_affected_index_entries)

更新记录

在写完undo之后,需要去更新索引记录,对于Lob列,调用函数 lob::update

  • 当修改的数据长度小于100字节时,走replace_inline(), 即直接修改对应的lob page
  • 否则,调用lob::replace(), 产生新版本的lob page

相关堆栈

lob::update()
|--> replace()
|--> first_page_t::replace_inline()
|--> data_page_t::replace_inline()

Read

根据worklog的描述,新的多去LOB多版本的逻辑变成了如下 (quoted from wl#11328):

1. Let clust_rec point to the latest clustered index record.
2. Using rollptr obtain the undo log record.
3. Construct the update vector from undo log record. 
   Save the update vector (in a queue) related to BLOBs for later use.
4. Using clust_rec and update vector, build older version of clustered
   index record.
5. Let clust_rec point to this version of clustered index record.
6. Check if clust_rec is the version needed.
   If yes, goto (7), otherwise goto (2).
7. Now fetch the BLOBs for clust_rec. Apply the update vectors matching
   the LOB version from the queue.

简而言之,主要是两个步骤:

  1. 从Undo log里读取binary diff信息,并产生update vector
trx_undo_prev_version_build
|-->trx_undo_update_rec_get_update
    |-->trx_undo_read_blob_update

这一步会将读到的数据存到一个lob::undo_vers_t

  1. 如果获得了正确的版本,在返回数据前,将update vector 应用到获得的记录中
row_sel_store_mysql_field_func
|-->lob::undo_vers_t::apply()
    |-->lob::undo_seq_t::apply()
        |-->lob::undo_data_t::apply()

Reference

WL#11328: InnoDB: Optimizing Small Changes to BLOBs
Partial update of JSON values
MySQL 8.0: InnoDB Introduces LOB Index For Faster Updates
MySQL 8.0: New Storage Format for Compressed BLOBs

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
MySQL8.0 · 引擎特性 · 关于undo表空间的一些新变化
Note: 当前版本为MySQL8.0.3 InnoDB的undo log是其实现多版本的关键组件,在物理上以数据页的形式进行组织。在早期版本中(
4342 0
MySQL · 引擎特性 · DROP TABLE之binlog解析
Drop Table的特殊之处 Drop Table乍一看,与其它DDL 也没什么区别,但当你深入去研究它的时候,发现还是有很多不同。最明显的地方就是DropTable后面可以紧跟多个表,并且可以是不同类型的表,这些表还不需要显式指明其类型,比如是普通表还是临时表,是支持事务的存储引擎的表还是不支持事务的存储引擎的表等。这些特殊之处对于代码实现有什么影响呢?对于普通表,无论是创建还是删除,数据库
2039 0
MySQL · 特性分析 · common table expression
common table expression Common table expression简称CTE,由SQL:1999标准引入, 目前支持CTE的数据库有Teradata, DB2, Firebird, Microsoft SQL Server, Oracle (with recursion since 11g release 2), PostgreSQL (since 8.4), Mar
2162 0
MySQL · 新特性分析 · 5.7中Derived table变形记
Derived table实际上是一种特殊的subquery,它位于SQL语句中FROM子句里面,可以看做是一个单独的表。MySQL5.7之前的处理都是对Derived table进行Materialize,生成一个临时表保存Derived table的结果,然后利用临时表来协助完成其他父查询的操作,比如JOIN等操作。MySQL5.7中对Derived table做了一个新特性。该特性允许将符合
6758 0
MySQL · 引擎特性 · Column Compression浅析
前言 当用户的数据量比较大时,通常需要对数据进行压缩,以减少磁盘占用。InnoDB目前有两种方式来实现这一目的。 第一种是传统的数据压缩,通过指定row_format及key_block_size,能够将用户表压缩到指定的page size并进行存储,默认使用zlib。这种压缩方式使用比较简单,但也是诟病较多的, 代码陈旧,相关代码基本上几个大版本都没发生过变化,一些优化点还是从faceboo
1652 0
数据库领域前沿技术分享与交流
热门文章
热门讨论
+关注
zhaiwx_yinfeng
MySQL内核开发者, 《高性能MySQL 第三版》译者之一,活跃于MySQL社区,BugList,etc...
文章
问答
视频
相关电子书
更多
Spark SQL最佳实践
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多