浅析InnoDB Record Header及page overflow(2)

简介: 浅析InnoDB Record Header及page overflow

3、案例测试验证过程

我们以场景18为例做测试。测试表结构见下横屏观看,下同)


[root@yejr.me]> CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c1` varchar(10) DEFAULT NULL,
  `c2` varchar(10) DEFAULT NULL,
  `c3` varchar(10) DEFAULT NULL,
  `c4` varchar(10) DEFAULT NULL,
  `c5` varchar(10) DEFAULT NULL,
  `c6` varchar(10) DEFAULT NULL,
  `c7` varchar(10) DEFAULT NULL,
  `c8` varchar(10) DEFAULT NULL,
  `c9` varchar(10) DEFAULT NULL,
  `c10` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;


插入两条数据

[root@yejr.me]>insert into t1 values(1,'a','a','a','a','a','a','a','a','a','a');

[root@yejr.me]>insert into t1 values(2,'b','b','b','b','b','b','b','b','b','b');


用innblock工具查看数据结构:

[root@yejr.me]# innblock innodb/t2_varchar.ibd 3 16
...
-----Total used rows:4 used rows list(phy):
(1) INFIMUM record offset:99 heapno:0 n_owned 1,delflag:N minflag:0 rectype:2
(2) SUPREMUM record offset:112 heapno:1 n_owned 3,delflag:N minflag:0 rectype:3
(3) normal record offset:137 heapno:2 n_owned 0,delflag:N minflag:0 rectype:0
(4) normal record offset:181 heapno:3 n_owned 0,delflag:N minflag:0 rectype:0


可以看到一条物理记录的长度是 181-137=44字节,分别由以下几部分组成:

  • record header:17字节
    • 其中基础RH=5
    • 共10个varchar列均允许为NULL因此需要额外10bit折算成2字节
    • 另外每个varchar列最大定义存储长度10*4=40,没超过256字节,每个varchar列需要额外1个字节,共10字节
    • 那么5+2+10=17字节
  • db_trx_id: 6字节
  • db_roll_ptr: 7字节
  • id列: 4字节
  • c1 ~ c10列:10字节

我们再用innodb_ruby工具验证一下:


[root@yejr.me]# innodb_space -s ibdata1 -T innodb/t1 -p 3 page-dump
...
records:
{:format=>:compact,
:offset=>137,
:header=>
{:next=>181,
:type=>:conventional,
:heap_number=>2,
:n_owned=>0,
:min_rec=>false,
:deleted=>false,
:nulls=>[],
:lengths=>
{"c1"=>1,
"c2"=>1,
"c3"=>1,
"c4"=>1,
"c5"=>1,
"c6"=>1,
"c7"=>1,
"c8"=>1,
"c9"=>1,
"c10"=>1},
:externs=>[],
:length=>17}, #所有record header总大小是17字节
:next=>181,
:type=>:clustered,
:key=>[{:name=>"id", :type=>"INT UNSIGNED", :value=>1}],
:row=>
[{:name=>"c1", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c2", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c3", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c4", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c5", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c6", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c7", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c8", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c9", :type=>"VARCHAR(40)", :value=>"a"},
{:name=>"c10", :type=>"VARCHAR(40)", :value=>"a"}],
:sys=>
[{:name=>"DB_TRX_ID", :type=>"TRX_ID", :value=>10518},
{:name=>"DB_ROLL_PTR",
:type=>"ROLL_PTR",
:value=>
{:is_insert=>true,
:rseg_id=>100,
:undo_log=>{:page=>491, :offset=>272}}}],
:length=>27, #row data总大小是27字节,加上RH的17字节,总共是44字节,和推测结果一致
:transaction_id=>10518,
:roll_pointer=>
{:is_insert=>true, :rseg_id=>100, :undo_log=>{:page=>491, :offset=>272}}}
...



4、什么时候发生page overflow

我们大概知道,一条记录由于有较多的变长列(varchar/text/blob类型),当长度约为page size的一半时,就会发生overflow,会把最长的那个列存储在独立的page中,聚集索引中用20字节的指针指向那个page(dynamic row format时是这么做的,不同row format处理方式也不同)。文档中是这么说的

Variable-length columns are an exception to the rule that column values are stored in B-tree index nodes. Variable-length columns that are too long to fit on a B-tree page are stored on separately allocated disk pages called overflow pages. Such columns are referred to as off-page columns. The values of off-page columns are stored in singly-linked lists of overflow pages, with each such column having its own list of one or more overflow pages. Depending on column length, all or a prefix of variable-length column values are stored in the B-tree to avoid wasting storage and having to read a separate page.

When a table is created with ROW_FORMAT=DYNAMIC, InnoDB can store long variable-length column values (for VARCHAR, VARBINARY, and BLOB and TEXT types) fully off-page, with the clustered index record containing only a 20-byte pointer to the overflow page. Fixed-length fields greater than or equal to 768 bytes are encoded as variable-length fields. For example, a CHAR(255) column can exceed 768 bytes if the maximum byte length of the character set is greater than 3, as it is with utf8mb4.


Whether columns are stored off-page depends on the page size and the total size of the row. When a row is too long, the longest columns are chosen for off-page storage until the clustered index record fits on the B-tree page. TEXT and BLOB columns that are less than or equal to 40 bytes are stored in line.

我们来测试下,一条记录长度到底是多少时,会发生overflow。测试表结构

[root@yejr.me]> CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`c1` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;



我们先进行反推,计算每条记录最长能存储多少字节:

  • 默认的page size是16KB,也就是16384字节
  • FIL Header消耗38字节
  • Index Header消耗36字节
  • File Segment Header消耗20字节
  • Infimum & Supremum 两条虚拟记录共消耗26字节
  • FIL Trailer消耗8字节
  • 由于此时page中最多只有两条物理记录,所以Page Directory消耗4字节
  • 每条记录还需要额外消耗DB_TRX_ID(6B)、DB_ROLL_PTR(7B)共13字节
  • 上述测试表的record header需要消耗7字节
  • 最后可算得每条记录中,text列最多不可超过(16384-38-36-20-26-8-4)/2-13-7-4=8102字节
  • 经过实测,text列最多可存储8101字节,一旦超过该值,就会发生overflow
  • 如果不预设表中各个列数据类型的话,当一条记录超过8125字节(含可变长度的record header,以及DB_TRX_ID、DB_ROLL_PTR的13字节在内)时,就会发生overflow

插入两条测试数据

[root@yejr.me]> insert into t1 select 1,repeat('a',8102);
[root@yejr.me]> insert into t1 select 2,repeat('a',8101);

用innblock工具解析

[root@yejr.me]# innblock innodb/t1.ibd 3 16
...
-----Total used rows:4 used rows list(phy):
(1) INFIMUM record offset:99 heapno:0 n_owned 1,delflag:N minflag:0 rectype:2
(2) SUPREMUM record offset:112 heapno:1 n_owned 3,delflag:N minflag:0 rectype:3
(3) normal record offset:127 heapno:2 n_owned 0,delflag:N minflag:0 rectype:0
(4) normal record offset:171 heapno:3 n_owned 0,delflag:N minflag:0 rectype:0
...


注意到第一条记录的长度是 171-127=44字节,可见的确是做overflow处理了。再用innodb_ruby工具解析整个page


[root@yejr.me]# innodb_space -s ibdata1 -T innodb/t1 -p 3 page-dump
...
records:
{:format=>:compact,
:offset=>127,
:header=>
{:next=>171,
...
:key=>[{:name=>"id", :type=>"INT", :value=>1}],
:row=>
[{:name=>"c1",
:type=>"BLOB",
:value=>"",
:extern=>{:space_id=>214, :page_number=>4, :offset=>38, :length=>8102}}],
...

{:format=>:compact,
:offset=>171,
:header=>
{:next=>112,
:type=>:conventional,
...
:key=>[{:name=>"id", :type=>"INT", :value=>2}],
:row=>
[{:name=>"c1",
:type=>"BLOB",
:value=>
"aaaaaaa...a"}],
:sys=>
[{:name=>"DB_TRX_ID", :type=>"TRX_ID", :value=>10619},
{:name=>"DB_ROLL_PTR",
:type=>"ROLL_PTR",
:value=>
{:is_insert=>true, :rseg_id=>36, :undo_log=>{:page=>466, :offset=>272}}}],
:length=>8118,
:transaction_id=>10619,
:roll_pointer=>
{:is_insert=>true, :rseg_id=>36, :undo_log=>{:page=>466, :offset=>272}}}
...

从page dump的结果能看到,第一条记录溢出存储在另一个page(pageno=4),溢出的字节数是8102,也就是全部放在独立的page里存储了,聚集索引中只保留了20字节的指针。

我们继续测试当表里有两个text列的情况

[root@yejr.me]> CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`c1` text NOT NULL,
`c2` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

[root@yejr.me]> insert into t1 select 1,repeat('a',100),repeat('a',8001);
[root@yejr.me]> insert into t1 select 2,repeat('a',100),repeat('a',8000);



用innblock工具解析


[root@yejr.me]# innblock innodb/t1.ibd 3 16
...
-----Total used rows:4 used rows list(phy):
(1) INFIMUM record offset:99 heapno:0 n_owned 1,delflag:N minflag:0 rectype:2
(2) SUPREMUM record offset:112 heapno:1 n_owned 3,delflag:N minflag:0 rectype:3
(3) normal record offset:128 heapno:2 n_owned 0,delflag:N minflag:0 rectype:0
(4) normal record offset:273 heapno:3 n_owned 0,delflag:N minflag:0 rectype:0
...


注意到第一条记录的长度是 273-128=145字节,可见的确是做overflow处理了。再用innodb_ruby工具解析整个page

[root@yejr.me]# innodb_space -s ibdata1 -T innodb/t1 -p 3 page-dump
...
{:format=>:compact,
:offset=>128,
:header=>
{:next=>273,
...
:key=>[{:name=>"id", :type=>"INT", :value=>1}],
:row=>
[{:name=>"c1",
:type=>"BLOB",
:value=>
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
{:name=>"c2",
:type=>"BLOB",
:value=>"",
:extern=>{:space_id=>215, :page_number=>4, :offset=>38, :length=>8001}}],
...
{:format=>:compact,
:offset=>273,
:header=>
{:next=>112,
...
:key=>[{:name=>"id", :type=>"INT", :value=>2}],
:row=>
[{:name=>"c1",
:type=>"BLOB",
:value=>
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
{:name=>"c2",
:type=>"BLOB",
:value=>
"a..."}],
:sys=>
[{:name=>"DB_TRX_ID", :type=>"TRX_ID", :value=>10686},
{:name=>"DB_ROLL_PTR",
:type=>"ROLL_PTR",
:value=>
{:is_insert=>true, :rseg_id=>78, :undo_log=>{:page=>478, :offset=>272}}}],
:length=>8117,
:transaction_id=>10686,
:roll_pointer=>
{:is_insert=>true, :rseg_id=>78, :undo_log=>{:page=>478, :offset=>272}}}


从page dump的结果能看到,第一条记录溢出存储在另一个page(pageno=4),溢出的字节数是8001,也是全部放在独立的page里存储了,聚集索引中只保留了20字节的指针。

好吧,测试案例就介绍到这里,更多的场景请自行测试。

P.S,如果想偷懒不自己测试,也可以看看我的 InnoDB Record Header消耗测试过程实录(文末提供链接)

P.P.S,我不是源码级MySQL内核开发者,水平有限,文中难免有误之处,还请多指教。

Enjoy MySQL :)

            </div>
相关文章
|
分布式计算 Hadoop 大数据
【大数据开发技术】实验05-HDFS目录与文件的创建删除与查询操作
【大数据开发技术】实验05-HDFS目录与文件的创建删除与查询操作
393 0
|
3月前
|
监控 Linux 数据安全/隐私保护
Python实现Word转PDF全攻略:从入门到实战
在数字化办公中,Python实现Word转PDF自动化,可大幅提升处理效率,解决格式兼容问题。本文详解五种主流方案,包括跨平台的docx2pdf、Windows原生的pywin32、服务器部署首选的LibreOffice命令行、企业级的Aspose.Words,以及轻量级的python-docx+pdfkit组合。每种方案均提供核心代码与适用场景,并涵盖中文字体处理、表格优化、批量进度监控等实用技巧,助力高效办公自动化。
803 0
|
11月前
|
人工智能 Cloud Native 大数据
跳出营销噱头,深度探索国内培训格局
企业培训从最初的技能补缺,到如今的战略性人才发展布局,它正在从“成本中心”转向“价值创造中心”。一些央企、国企、上市公司更是将培训作为战略推动器,以培养具备全球化视野、数字化思维和创新精神的人才队伍。好的企业培训公司,就像一台高性能引擎,不仅帮助组织提速增效,更能在关键节点实现人才梯队的“基因升级”。
|
存储 安全 数据安全/隐私保护
备份SaaS数据的5个原因
【10月更文挑战第28天】SaaS虽被认为是几乎全能的解决方案,但企业仍需对业务和利润保持警惕。备份SaaS数据至关重要,原因包括:恢复过程可能缓慢;服务条款可能变化;云端数据并非永恒;人为错误难以避免;以及防止信息失控。因此,企业应确保有独立备份以应对各种风险。
247 0
|
11月前
|
人工智能 数据可视化 API
FastGPT 基于Higress 聚合 LLM 网关的最佳实践
本文介绍了Fast GPT的产品形态和设计理念,重点讨论了大模型的幻觉问题及其对应用落地的影响。Fast GPT通过结合工作流的强逻辑性和AI的理解能力,提升系统的稳定性和可靠性。文章还详细描述了Fast GPT的工作流节点、知识库管理及AI网关的功能,并展示了几个实际应用场景,如私人助手、图文生成和文档处理等。最后,探讨了如何通过引入云函数和Copilot简化代码编写,实现无代码编排的工作流解决方案,提升用户体验。
|
JSON 小程序 前端开发
微信小程序-人脸核身解决方案
微信小程序-人脸核身解决方案
1400 0
|
人工智能
初涉歌词创作:写歌词的技巧和方法你知道多少,妙笔生词智能写歌词软件
对于初涉歌词创作的人来说,掌握关键技巧和使用“妙笔生词智能写歌词软件”能大大提升创作效率。该软件提供AI智能写词、押韵优化、歌词续写等功能,帮助捕捉灵感,明确主题,优化结构,使语言表达更加简洁生动,助你创作出感人至深的作品。
|
存储 运维 物联网
长安汽车×云器Lakehouse一体化数据平台,成本降低50%,建立智能互联时代的领先优势
长安汽车智能化研究院致力于汽车智能化技术研究,通过构建基于云器科技Lakehouse一体化数据平台,解决了高并发、大规模车联网数据处理难题,实现了数据实时写入、高效分析和成本优化,助力汽车智能驾驶、网联和交通全面发展。
407 0
长安汽车×云器Lakehouse一体化数据平台,成本降低50%,建立智能互联时代的领先优势
|
运维 监控 Devops
|
Web App开发 编解码 前端开发
更专业省心的来了,你没必要研究UE4和Unity官方推流了!
需求催生了一种新的“云渲染”方案,将庞大负载的“渲染”放到云端强大算力的服务器去执行,前端仅仅是展示和交互。 本文小编介绍了目前主流的3种方案,如果你感兴趣的话一起看看吧~
更专业省心的来了,你没必要研究UE4和Unity官方推流了!