百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

InnoDB行结构详解

bigegpt 2024-08-18 14:02 2 浏览

在上一篇《MySQL体系架构》中讲到MySQL的整体体系架构,但是受到篇幅限制,不能够深入细节讲解,因此针对网友问题比较集中的点做一个详细的讲解。

InnoDB默认的行格式是Dynamic,本文则以Compact为例介绍,两者本身也是及其相似的。我们先来回顾一下Compact行结构,如下:

变长字段长度列表

“变长”顾名思义这里只存储不固定长度的字段长度值,MySQL变长的字段类型有,VARCHAR、VARBINARY、TEXT、BLOB等类型。

接下来为了方便阅读我们将变长字段长度列表简称为变长列表

目的

Compact行中记录头信息、RowID、事务ID、回滚指针都是有固定字节数的,但是列数据占用的实际长度是无法计算的,因为数据列中可能存在变长的字段类型,那么我们就无法计算整个行的精确长度了,那么就不知道行与行之间的边界了。因此我们需要通过变长列表来记录变长的字段所占用的字节数。

规则

  1. 变长列表的记录是按照变长列逆序存放的,在上一篇文章中有示例。
  2. 如果变长字段的值为NULL,则会记录在NULL值列表,变长列表不记录。
  3. 变长字段的定义长度<=255字节使用1字节存储长度,否则用2字节。

例如:创建列类型为varchar(m),字符集为utf8,则一个字符占用3字节,则定义长度为m*3<=255,其它字符集不在赘述(MySQL5.0之后将varchar(m)的m由字节数调整为字符数)。

温馨提示:

从这个规则可以看出,在设计列时,非必要可以减少变长字段的使用,这样可以有效减少变长列表长度。

如果使用到变长类型如varchar(m),其中m的值尽量设置为预计存储字符数的最大长度,不要定义太过随意,可能会影响行的相关计算性能,因为小于等于255字节,说明其长度不会超过1111 1111(一字节表示的最大无符号整数),则直接使用1字节,当大于255时会多一条规则计算,即规则4。

4. 变长字段真实长度<=127字节使用1字节存储长度,否则为两字节。

为什么是127呢?

InnoDB设计者非常的睿智,他将第一个字节的最高位一位用来标记是否是连续的两字节表示变长字段长度,即高位为0表示字段长度占用1字节,若为1,则需要再向前读取一个字节。但是有个特殊情况,规则3中提到的255字节,255二进制为1111 1111,这就和规则4冲突了,因此需要优先判断规则3。

127的二进制为0111 1111,那么再加1则变成128(1000 0000),按照规则,必须存两个字节,即00 80。最高位既要作为标识位,又要作为数据位,那么127就是边界值了,否则我们需要对最高位做翻转才能得到实际的长度值,但是翻转又会导致改变其作为数据位时的值(比如长度01 ff和01 7f都要记录为01 ff,那么这两个值就分不清了)。

下面我们着重讲一下变长列表的读取方法。

如何读取变长列表?

变长列表的存储长度值是连续的,我们如何区分出每个长度值呢?

我们先列出一个结论,带着这个结论看下面的示例:

结论:变长列表为逆序存放逆序读取,逆序读取的第一个字节,首先需要判断字段定义长度是否超过255字节,若没有超过则读取的第一字节即为长度值,否则,如果为1则表示还要向前读取一个字节,两个字节值则为变长列长度;若高位为0则该字节值即为变长字段的长度。

这是我建立的一张表(row_format=COMPACT engine=innodb charset=ascii),有5个列,插入的数据如下图:

然后通过命令行的一些列操作(具体怎么操作的,我后续出个教程,今天肝不动了),得到了其ibd文件的数据如下:

然后我们可视化一下,得到如下格式(为了减少文字表述,我直接在图上做了文字标注):

我们重点看一下变长列表:02 03代表什么呢?我们看一下变长字段有col1、col3、col4,根据规则2我们把col3过滤掉,剩下col1、col4,值分别为aaa、cc,按照规则1可得出02 03。假如我们在表最前面插入一个新列col0:varchar(512),其值等于128字节,则变长列表变为:02 03 00 80。

我们找到一个数据行要么是通过槽,要么是通过上一条行记录头中的next_record指针,这两者都是指向的行记录第一个列的位置。如果知道了第一个列的位置,我们就可以向左侧搜索,找到的就是记录头(固定大小)和NULL标记位(可计算出占用大小,下面会讲到),减去上他们占用的字节数就可以计算出变长列表的最右侧起始位置了。上面提到变长列表为:02 03 00 80,我们首先读到80,首先通过表的定义信息获取第一个变长列的定义长度为512,则直接判断高位,发现其高位为1,则读下一个字节,最终其实际长度00 80。

有没有发现(结合上图看),其检索方式就是双指针,一个向左读变长列表,一个向右读各个列,这里左指针读取变长列表中的列长度值顺序是和右指针读取列值的顺序是一样的,这就是为什么变长列表要逆序存储的原因。综上所述,这也是为什么变长列表要逆序读取的原因。

上面讲得有那么点啰嗦了,但是为了讲清楚,你们忍一下,务必认真读一遍。

NULL标记位

前面讲了变长列表,其中是没有记录NULL的变长列的,那么NULL就会做一个非常重要的补充记录,记录所有允许为NULL且列值实际为NULL的列。

目的

为了让我们能够正确的读取列值,我们通过n个字节来记录所有NULL值的列。

规则

  1. 只有字段值允许为NULL的字段才会记录在NULL值标记位中。
  2. NULL值标记位是有0~n字节组成的,每个字节中的位就代表了列是否为NULL值,即0代表不为NULL,1代表为NULL。
  3. 当所有列都不允许为空时,则没有该标记位。

温馨提示:建表的时候尽量减少允许NULL字段的出现,最好是都不允许为NULL,这样每个记录行会节约最少1个字节的存储空间。

4. NULL值标记位中位是按照列的逆序存储的,读取方向与变长列表一样,见下图:

5. NULL标记位的计算逻辑为:向上取整(允许为NULL的列数量/8)=NULL标记字节数。

固定头信息

头记录信息中包含了该行在页中的位置、行相对于上一条行记录的位置等一系列位置及状态信息。

名称

占用位数(bit)

功能描述

预留位

1

预留位

预留位

1

预留位

delete_flag

1

删除标识

min_rec_flag

1

非叶子节点最小目录项记录标识

n_owned

4

若为分组的头号记录,则记录组内的记录数,每组4-8个记录

heap_no

13

当前记录在页中的相对位置

record_type

3

记录类型,0:表示普通记录;1:表示B+树非叶子节点的目录项记录;2:表示Infimum记录;3:表示Supremum记录;

next_record

16

下一条记录的相对位置

隐藏列

隐藏列例如:row_id(行的Primary Key,若没指定则有会生成该列)、trx_id(事务ID)、roll_pointer(回滚指针),这些都是MySQL事务及MVCC实现必不可少的数据,由MySQL内部维护,后续在介绍事务及过版本控制协议MVCC会详细讲到。

名称

占用字节数

功能描述

row_id

6

行号

trx_id

6

事务ID

roll_pointer

7

回滚指针

Dynamic格式与Compact格式

这里简单介绍一下Dynamic格式与Compact格式异同:

  • Dynamic格式支持对大型动态字段的页外存储能力,而聚集索引节点只需要记录其溢出页面的指针,长度20字节(这也是上面没有提到的Compact格式可能存在跨页存储的问题,解决方案两种格式都是类似的)。
  • 因其上述特性,其对大型索引键前缀的支持能力就显而易见了(索引值页外存储,最大支持3072字节,这个长度受innodb_large_prefix配置控制)。
  • Dynamic格式对小型字段的支持仍然与Compact完全一致。

Infimun和Supremum行

借这篇文章在这里顺便介绍一下,这两个记录就是确定一个页内行记录的存储边界的,在图:ibd文件(16进制)中也可以直观看到,他们在所有数据行的最前面,他们的next_recoerd指针分别指向内内第一个行记录与最后一个行记录。

总结

  • 创建表的时候,尽量减少动态列的使用(尤其是TEXT类型),可以使用char或者其它类型代替,既可以节约存储空间,又可以提升行检索计算性能。
  • 如果大量使用到变长字段类型,也需要合理地计算其字符数,不要定义太过随意,以免影响计算性能。
  • 尽量不要建允许为NULL的列,这样既可以减少行存储空间,又可以提升计算性能。

以上就是对Compact行的详细介绍了,如果有疑问或者不对的地方欢迎在评论区指出,如果你看到这篇文章有收获就请给作者一个关注+点赞吧[送心]!

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...