掘金课程-InnoDB记录存储结构

准备工作

到现在为⽌, MySQL 对于我们来说还是⼀个⿊盒,我们只负责使⽤客户端发送请求 并等待服务器返回结果,表中的数据到底存到了哪⾥?以什么格式存放的? MySQL 是以什么⽅式来访问的这些数据?这些问题我们统统不知道,对于未知领域的探索向 来就是社会主义核⼼价值观中的⼀部分,作为新⼀代社会主义接班⼈,不把它们搞懂 怎么⽀援祖国建设呢? 我们前边唠叨请求处理过程的时候提到过, MySQL 服务器上负责对表中数据的读取 和写⼊⼯作的部分是 存储引擎 ,⽽服务器⼜⽀持不同类型的存储引擎,⽐如 InnoDB 、 MyISAM 、 Memory 啥的,不同的存储引擎⼀般是由不同的⼈为实现不同 的特性⽽开发的,真实数据在不同存储引擎中存放的格式⼀般是不同的,甚⾄有的存 储引擎⽐如 Memory 都不⽤磁盘来存储数据,也就是说关闭服务器后表中的数据就消 失了。由于 InnoDB 是 MySQL 默认的存储引擎,也是我们最常⽤到的存储引擎,我
们也没有那么多时间去把各个存储引擎的内部实现都看⼀遍,所以本集要唠叨的是使 ⽤ InnoDB 作为存储引擎的数据存储结构,了解了⼀个存储引擎的数据存储结构之 后,其他的存储引擎都是依葫芦画瓢,等我们⽤到了再说哈~

InnoDB⻚简介

InnoDB 是⼀个将表中的数据存储到磁盘上的存储引擎,所以即使关机后重启我们的数据还是存在的。⽽真正处理数据的过程是发⽣在内存中的,所以需要把磁盘中的数 据加载到内存中,如果是处理写⼊或修改请求的话,还需要把内存中的内容刷新到磁 盘上。⽽我们知道读写磁盘的速度⾮常慢,和内存读写差了⼏个数量级,所以当我们 想从表中获取某些记录时, InnoDB 存储引擎需要⼀条⼀条的把记录从磁盘上读出来么?不,那样会慢死, InnoDB 采取的⽅式是:将数据划分为若⼲个⻚,以⻚作为磁 盘和内存之间交互的基本单位,InnoDB中⻚的⼤⼩⼀般为 16 KB。也就是在⼀般情 况下,⼀次最少从磁盘中读取16KB的内容到内存中,⼀次最少把内存中的16KB内容 刷新到磁盘中。

InnoDB⾏格式

我们平时是以记录为单位来向表中插⼊数据的,这些记录在磁盘上的存放⽅式也被称为 ⾏格式 或者 记录格式 。设计InnoDB 存储引擎的⼤叔们到现在为⽌设计了4种不同类型的⾏格式 ,分别是 CompactRedundantDynamicCompressed ⾏格式,随着时间的推移,他们可能会设计出更多的⾏格式,但是不管怎么变,在原理上 ⼤体都是相同的。

指定行格式的语法

我们可以在创建或修改表的语句中指定⾏格式

CREATE TABLE 表名 (列的信息) ROW_FORMAT=⾏格式名称

ALTER TABLE 表名 ROW_FORMAT=⾏格式名称

InnoDB目前定义了四种行格式

  • Compact行格式

在这里插入图片描述

变⻓字段⻓度列表

我们知道 MySQL ⽀持⼀些变⻓的数据类型,⽐如 VARCHAR(M) 、 VARBINARY(M) 、 各种 TEXT 类型,各种 BLOB 类型,我们也可以把拥有这些数据类型的列称为 变⻓字 段 ,变⻓字段中存储多少字节的数据是不固定的,所以我们在存储真实数据的时候 需要顺便把这些数据占⽤的字节数也存起来,这样才不⾄于把 MySQL 服务器搞懵, 所以这些变⻓字段占⽤的存储空间分为两部分: ①真正的数据内容 ②占⽤的字节数
在 Compact ⾏格式中,把所有变⻓字段的真实数据占⽤的字节⻓度都存放在记录的 开头部位,从⽽形成⼀个变⻓字段⻓度列表,各变⻓字段数据占⽤的字节数按照列的 顺序逆序存放,我们再次强调⼀遍,是逆序存放!
我们拿 record_format_demo 表中的第⼀条记录来举个例⼦。因为 record_format_demo 表的 c1 、 c2 、 c4 列都是 VARCHAR(10) 类型的,也就是变 ⻓的数据类型,所以这三个列的值的⻓度都需要保存在记录开头处,因为 record_format_demo 表中的各个列都使⽤的是 ascii 字符集,所以每个字符只需 要1个字节来进⾏编码,来看⼀下第⼀条记录各变⻓字段内容的⻓度:

列名存储内容内容长度(十进制表示)内容长度(十六进制表示)
c1‘aaaa’40x04
c2‘bbb’30x03
c4‘d’10x01

由于第⼀⾏记录中 c1 、 c2 、 c4 列中的字符串都⽐较短,也就是说内容占⽤的字 节数⽐较⼩,⽤1个字节就可以表示,但是如果变⻓列的内容占⽤的字节数⽐较多, 可能就需要⽤2个字节来表示。具体⽤1个还是2个字节来表示真实数据占⽤的字节 数, InnoDB 有它的⼀套规则,我们⾸先声明⼀下 W 、 M 和 L 的意思:

  1. 假设某个字符集中表示⼀个字符最多需要使⽤的字节数为 W ,也就是使⽤ SHOW CHARSET 语句的结果中的 Maxlen 列,⽐⽅说 utf8 字符集中的 W 就是 3 , gbk 字符集中的 W 就是 2 , ascii 字符集中的 W 就是 1 。
  2. 对于变⻓类型 VARCHAR(M) 来说,这种类型表示能存储最多 M 个字符(注意是 字符不是字节),所以这个类型能表示的字符串最多占⽤的字节数就是 M×W 。
  3. 假设它实际存储的字符串占⽤的字节数是 L 。 所以确定使⽤1个字节还是2个字节表示真正字符串占⽤的字节数的规则就是这样: 如果 M×W <= 255 ,那么使⽤1个字节来表示真正字符串占⽤的字节数。 如果 M×W > 255 ,则分为两种情况: 如果 L <= 127 ,则⽤1个字节来表示真正字符串占⽤的字节数。 如果 L > 127 ,则⽤2个字节来表示真正字符串占⽤的字节数。

另外需要注意的⼀点是,变⻓字段⻓度列表中只存储值为 ⾮NULL 的列内容占⽤的 ⻓度,值为 NULL 的列的⻓度是不储存的 。也就是说对于第⼆条记录来说,因为 c4 列的值为 NULL ,所以第⼆条记录的 变⻓字段⻓度列表 只需要存储 c1 和 c2 列的 ⻓度即可。其中 c1 列存储的值为 ‘eeee’ ,占⽤的字节数为 4 , c2 列存储的值 为 ‘fff’ ,占⽤的字节数为 3 。数字 4 可以⽤1个字节表示, 3 也可以⽤1个字节表 示,所以整个 变⻓字段⻓度列表 共需2个字节。填充完 变⻓字段⻓度列表 的两条记录的 对⽐图如下:
在这里插入图片描述

NULL值列表

我们知道表中的某些列可能存储 NULL 值,如果把这些 NULL 值都放到 记录的真实数 据 中存储会很占地⽅,所以 Compact ⾏格式把这些值为 NULL 的列统⼀管理起来, 存储到 NULL 值列表中,它的处理过程是这样的:

  1. ⾸先统计表中允许存储 NULL 的列有哪些。 我们前边说过,主键列、被 NOT NULL 修饰的列都是不可以存储 NULL 值的,所以在统计的时候不会把这些列算进去。⽐⽅说表 record_format_demo 的3个 列 c1 、 c3 、 c4 都是允许存储 NULL 值的,⽽ c2 列是被 NOT NULL 修饰,不允许存储 NULL 值。
  2. 如果表中没有允许存储 NULL 的列,则 NULL值列表 也不存在了,否则将每个允许存储 NULL 的列对应⼀个⼆进制位,⼆进制位按照列的顺序逆序排列,⼆进制位表示的意义如下:
    ⼆进制位的值为 1 时,代表该列的值为 NULL 。 ⼆进制位的值为 0 时,代表该列的值不为 NULL 。
    ⼩贴⼠: 并不是所有记录都有这个 变⻓字段⻓度列表 部分,⽐⽅说表中所有的列都不是变⻓的数据类型的话,就没有这⼀部分 !
  3. MySQL 规定 NULL值列表 必须⽤整数个字节的位表示,如果使⽤的⼆进制位个数 不是整数个字节,则在字节的⾼位补 0 。
记录头信息

除了 变⻓字段⻓度列表 、 NULL值列表 之外,还有⼀个⽤于描述记录的 记录头信息 , 它是由固定的 5 个字节组成。 5 个字节也就是 40 个⼆进制位,不同的位代表不同 的意思,如图:
在这里插入图片描述

名称大小(单位:bit)描述
预留位11没有使⽤
预留位21没有使⽤
delete_mask1标记该记录是否被删除
min_rec_mask1B+树的每层⾮叶⼦节点中的最⼩记录都会添加该标记
n_owned4表示当前记录拥有的记录数
heap_no13表示当前记录在记录堆的位置信息
record_type3表示当前记录的类型, 0 表示普通记录, 1 表示B+树⾮ 叶⼦节点记录, 2 表示最⼩记录, 3 表示最⼤记录
next_record16表示下⼀条记录的相对位置
记录的真实数据

对于 record_format_demo 表来说, 记录的真实数据 除了 c1 、 c2 、 c3 、 c4 这⼏ 个我们⾃⼰定义的列的数据以外, MySQL 会为每个记录默认的添加⼀些列(也称为 隐藏列 ),具体的列如下:

列名是否必须占用空间描述
row_id6 字节⾏ID,唯⼀标识⼀条记录
transaction_id6 字节事务ID
roll_pointer7 字节回滚指针
  • Redundant行模式
    在这里插入图片描述
字段长度偏移列表

注意 Compact ⾏格式的开头是 变⻓字段⻓度列表 ,⽽ Redundant ⾏格式的开头 是 字段⻓度偏移列表 ,与 变⻓字段⻓度列表 有两处不同: 没有了变⻓两个字,意味着 Redundant ⾏格式会把该条记录中所有列(包 括 隐藏列 )的⻓度信息都按照逆序存储到 字段⻓度偏移列表 。 多了个偏移两个字,这意味着计算列值⻓度的⽅式不像 Compact ⾏格式那么 直观,它是采⽤两个相邻数值的差值来计算各个列值的⻓度。

记录头信息
名称大小(单位:bit)描述
预留位11没有使⽤
预留位21没有使⽤
delete_mask1标记该记录是否被删除
min_rec_mask1B+树的每层⾮叶⼦节点中的最⼩记录都会添加该标 记
n_owned4表示当前记录拥有的记录数
heap_no13表示当前记录在⻚⾯堆的位置信息
n_field10表示记录中列的数量
1byte_offs_flag1标记字段⻓度偏移列表中每个列对应的偏移量是使 ⽤1字节还是2字节表示的
next_record16表示下⼀条记录的绝对位置
  • Dynamic和Compressed⾏格式
    这两种⾏格式类似于 COMPACT⾏格式 ,只不过在处理⾏溢出数据时有点⼉ 分歧,它们不会在记录的真实数据处存储字符串的前768个字节,⽽是把 所有的字节都存储到其他⻚⾯中,只在记录的真实数据处存储其他⻚⾯的
    地址。 另外, Compressed ⾏格式会采⽤压缩算法对⻚⾯进⾏压缩。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值