Mysql-InnoDB记录结构

InnoDB页简介

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

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

COMPACT行格式
一条完整的记录其实可以被分为 记录的额外信息 记录的真实数据 两大部分,下边我
们详细看一下这两部分的组成。
 记录的额外信息
这部分信息是服务器为了描述这条记录而不得不额外添加的一些信息,这些额外信息分为3类,分别是 变长字段
长度列表 NULL值列表 记录头信息 ,我们分别看一下。

我们知道 MySQL 支持一些变长的数据类型,比如 VARCHAR(M) VARBINARY(M) 、各种 TEXT 类型,各种 BLOB
型,我们也可以把拥有这些数据类型的列称为 变长字段 ,变长字段中存储多少字节的数据是不固定的,所以我
们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来,这样才不至于把 MySQL 服务器搞懵,所以
这些变长字段占用的存储空间分为两部分:
1. 真正的数据内容
2. 占用的字节数
Compact 行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长
字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放,我们再次强调一遍,是逆序存放!
我们拿 record_format_demo 表中的第一条记录来举个例子。因为 record_format_demo 表的 c1 c2 c4
都是 VARCHAR(10) 类型的,也就是变长的数据类型,所以这三个列的值的长度都需要保存在记录开头处,因为
record_format_demo 表中的各个列都使用的是 ascii 字符集,所以每个字符只需要1个字节来进行编码,来看
一下第一条记录各变长字段内容的长度:列名 存储内容 内容长度(十进制表示) 内容长度(十六进制表示)
c1 'aaaa' 4 0x04
c2 'bbb' 3 0x03
c4 'd' 1 0x01
又因为这些长度值需要按照列的逆序存放,所以最后 变长字段长度列表 的字节串用十六进制表示的效果就是
(各个字节之间实际上没有空格,用空格隔开只是方便理解):
01 03 04
把这个字节串组成的 变长字段长度列表 填入上边的示意图中的效果就是:
由于第一行记录中 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个字节来表示真正字符串占用的字节数。
也就是说InnoDB在读记录的变长字段长度列表时先查看表结构,如果某个变长字段允许存储的最
大字节数不大于255时,可以认为只使用1个字节来表示真正字符串占用的字节数。
如果 M×W > 255 ,则分为两种情况:
如果 L <= 127 ,则用1个字节来表示真正字符串占用的字节数。
如果 L > 127 ,则用2个字节来表示真正字符串占用的字节数。
InnoDB在读记录的变长字段长度列表时先查看表结构,如果某个变长字段允许存储的最大字节
数大于255时,该怎么区分它正在读的某个字节是一个单独的字段长度还是半个字段长度呢?
设计InnoDB的大叔使用该字节的第一个二进制位作为标志位:如果该字节的第一个位为0,那
该字节就是一个单独的字段长度(使用一个字节表示不大于127的二进制的第一个位都为0),
如果该字节的第一个位为1,那该字节就是半个字段长度。
对于一些占用字节数非常多的字段,比方说某个字段长度大于了16KB,那么如果该记录在单个
页面中无法存储时,InnoDB会把一部分数据存放到所谓的溢出页中(我们后边会唠叨),在变
长字段长度列表处只存储留在本页面中的长度,所以使用两个字节也可以存放下来。
总结一下就是说:如果该可变字段允许存储的最大字节数( M×W )超过255字节并且真实存储的字节数( L
超过127字节,则使用2个字节,否则使用1个字节。另外需要注意的一点是,变长字段长度列表中只存储值为 NULL 的列内容占用的长度,值为 NULL 的列的长度
是不储存的 。也就是说对于第二条记录来说,因为 c4 列的值为 NULL ,所以第二条记录的 变长字段长度列表
需要存储 c1 c2 列的长度即可。其中 c1 列存储的值为 'eeee' ,占用的字节数为 4 c2 列存储的值
'fff' ,占用的字节数为 3 。数字 4 可以用1个字节表示, 3 也可以用1个字节表示,所以整个 变长字段长度
列表 共需2个字节。

并不是所有记录都有这个 变长字段长度列表 部分,比方说表中所有的列都不是变长的数据类型的话,
这一部分就不需要有。

posted @ 2019-11-06 09:09  OuA  阅读(232)  评论(0编辑  收藏  举报