小册总结系列-第五章-innodb行记录

381 阅读4分钟

innodb行记录

本文是对小册MySQL是怎样运行的:从根儿上理解MySQL中第五章的总结。关于一些细节可以购买小册查看。

以下大多数内容是基于compact行格式讲述,使用其它格式会有特别说明。

变长字段长度列表

MySQL建表的时候会指定字段类型,字段类型中包括变长字段,如:varchar(10)。假如存储的数据没有10个字符,这时候数据库不会占用磁盘10个字符的所需的空间,只会占用实际记录所需的空间+行记录额外信息

建表时就会确定表字段的字符集,不同字符集每个字符所占用的字节数不同,如:utf8字符集中每个字符占用3个字节,gbk字符集中每个字符占用2个字节,ascii字符集中每个字符占用1个字节。

每一行记录存储的时候,都会存储一个列表,该列表的内容就是变长字段实际的长度列表,与行记录的顺序是相反的。

假如一个字段存储的内容,超过了256字节,便意味着变长字段长度列表中,该字段的数值超过了1字节,需要两个字节来表示。存储一个数值需要多少字节,检索的时候如何判断,在小册中有详细的解释,感兴趣的可以去看这里

这样做的目的是,可以通过检索变长字段列表就去找到存储的数据。不过变长字段长度列表中只存储值为非NULL的列内容占用的长度,值为NULL的列会存储在null值列表中。所以需要结合null值列表才能准确的检索到某个字段的内容。

null值列表

没有被not null修饰过的字段,才可以出现在该列表中; 该列表的顺序和行记录的顺序是相反的(和变长字段列表规则一样); null用1表示,非null用0表示; 图文的详细说明看这里

记录头信息

记录头由40个二进制位,不同的位代表不同的意思,具体信息如下表(表格来源

名称 大小(单位:bit) 描述
预留位1 1 没有使用
预留位2 1 没有使用
delete_mask 1 标记该记录是否被删除
min_rec_mask 1 B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned 4 表示当前记录拥有的记录数
heap_no 13 表示当前记录在记录堆的位置信息
record_type 3 表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录
next_record 16 表示下一条记录的相对位置

每一行记录的真实数据中,除了以上的内容,还包括隐藏列,隐藏列如下表(表格来源

列名 是否必须 占用空间 描述
DB_ROW_ID 6字节 行ID,唯一标识一条记录
DB_TRX_ID 6字节 事务ID
DB_ROW_ID 7字节 回滚指针

行溢出数据

行溢出原因:

在MySQL中,一行记录的内容有:变长字段长度列表,null值标识,真实数据。 一行记录最多可以存放65535字节数据,假设一张表只有一个字段,该字段允许null值。 则一行记录的最大分配方式为:存放65532字节真实数据。因为,null值列表占用1字节,变长字段列表占用2字节。如果存放更多的数据MySQL会报错。 在MySQL中一个页(page)的大小为16KB(即16384字节),所以会出现一行记录数据大小超过一页。MySQL通过行溢出来处理这种情况。

MySQL处理方式:

MySQL对于占用存储空间非常大的列,在记录的真实数据处只会存储该列前768个字节 + 20个字节存储指向其它页的地址(这20个字节中还包括这些分散在其他页面中的数据的占用的字节数)

关于768字节如何验证以及20个字节具体有些什么可以看这篇文章

MySQL中页数据的分配(具体怎么计算的,看这里 ):

每行记录的信息,除了真实数据之外,还有27字节行记录额外数据。 每个页除了需要存放实际的数据之外,还需要132个字节的空间来存放额外信息。 MySQL规定一个页中至少存放两行记录。 所以,当某列记录大于一个临界点时,就会出现行溢出。


Dynamic和Compressed行格式

这两种行格式,在行溢出时,不会存储768字节真实数据,而是全部数据都存放在其它页中。Compressed还会用压缩算法压缩数据,顺便提一句,如果使用Compressed行格式,对cpu的消耗会多一些。