InnoDB(4.2) 行记录格式

行记录格式

现在的MySQL支持4种行记录格式

  • Redundant
  • Compact
  • Dynamic
  • Compressed

Compact行记录格式

Compact行记录可以高效地存储数据,即一个页种如果存放的行数据越多,其性能就越高。

下图为Compact行记录的存储方式
在这里插入图片描述

变长字段长度列表

Compact行记录格式的首部是一个变长字段长度列表,该列表是非NULL的,并且是按照列的顺序逆序处置的

变长字段就是指字段类型为VARCHAR、VARBINARY,这些可以动态扩展的列。

举个栗子

//创建一个表
create table me(
	a varchar(10),
    b varchar(10),
    c char(10),
    d varchar(10)
)engine=innodb;
//插入数据
insert into select a,bb,ccc,dddd;

那么插入的第一条记录的变长字段列表为[04,02,01],分别对应dddd,bb,a的长度(逆序排可变长度列)

其长度有如下两条规则

  • 若列的长度小于255( 2 8 2^8 28),占用一个字节表示(一个字节刚好8位)。
  • 如果超过255,那就占用两个字节表示,2个字节就是16位,也就是65536,varchar最大长度就是16位,超过了就会变成Text类型。
NULL标志位

该位指示了该行数据中是否有NULL值,有则使用1来表示,该部分占的字节大小大概为1字节,比如当NULL标志位为06时,06转换为二进制为110,表示第二列和第三列为NULL(如果超过了8列会怎么办?)。

记录头信息

记录头信息固定占5字节(40位)

详细如下

名称大小(位)描述
()1未知
()1未知
deleted_flag1该行是否被删除
min_rec_flag1如果该记录是预先被定义为最小的记录,值为1
n_owned4该记录拥有的记录数
heap_no13索引堆中该条记录的排序记录
record_type3记录该行记录的类型,000表示普通,001表示B+树的结点指针。。。
next_record16页中下一条行记录的相对未知
Total40

记录头信息(Record Header)的最后两个字节就是next_record,next_record代表下一个记录的偏移量,即当前记录的位置加上next_record的偏移量就是下一条行记录的起时位置,所以在页的内部,实则上也是通过一种链表的形式来串连各条行记录的

存储列的部分

最后的部分就是实际存储每个列的数据,这里需要注意的是,NULL不占该部分的任何空间,NULL除了占有NULL标志位的1字节空间外,实际储存是不占有任何空间的。

另外一点要注意的是,每行数据除了拥有用户定义的列外,其实还会有两个隐藏列,为事务ID列和回滚指针列,分别为6字节和7字节的大小,同时,若InnoDB表没有设置主键,需要使用到rowid,还会有一个6字节的rowid列

实战看一下里面Compact记录

这是b表的数据

其中a 、c、e都是varchar型,而d、f是Int型
在这里插入图片描述
现在来查看一下b.ib里面的情况

hexdump -C bibd //linux命令

在这里插入图片描述
红色框住的一部分就是表中第一行的数据

可以看到04 03 01对应的就是eeee,ccc,a的长度(也是反序)

然后04就是NULL标志位,04转换成二进制就是0010,即第一第二列不为空,第三列为空,第四列不为空,正好也是对应行的数据

Redundant行记录

这种行记录是为了兼容5.0版本之前而存在的,采用了以下格式进行储存
在这里插入图片描述
Redundant行记录与Compact行记录最大的区别就是少了字段长度偏移列表,其他都是比较类似的。

字段长度偏移列表

Redundant行记录第一个部分是字段长度偏移列表,但与Compact行记录不一样(Compact是变长字段长度偏移列表),同样是按照列的顺序进行逆序放置的,同样拥有下面两条规则,

  • 若列的长度小于255(字符),占用1个字节表示
  • 若大于255,占用2个字节表示
  • 与Compact不同,Redundant存储的是所有列的长度,包含NULL列,NULL列如果是可变长度类型的话,记录为0,如果不是可变长度会有长度。
名称大小描述
()1未知
()1未知
deleted_flag1该行是否以被删除
min_rec_flag1该记录是预先被定义为最小的记录时,该字段为1
n_owned4该记录拥有的记录数
heap_no13索引堆中该条记录的索引号
n_fields10该行记录中列的数量(与Compact不同)
1byte_offs_flag1偏移列表为1字节还是2字节(与Compact不同)
next_record16页中下一条行记录的相对位置
Total48
存储列的部分

存储列的部分与Compact格式基本一样,同样隐藏了2个列(事务ID列和回滚指针列),除了对NULL值的处理。

对NULL值的处理

Redundant对NULL值的处理可能会占用空间的,在前面的字段长度偏移字段里面是有记录NULL值长度大小的,如果为可变类型才为0,且该NULL值在存储列部分不占空间,但有些比如char类型,为NULL的话,在字段长度偏移字段是有长度大小的(不为0,具体根据建表时定义的长度计算),对于该NULL值,在存储列部分是占空间的。

行溢出数据

InnoDB存储引擎可以将一条记录中的某些数据存储在真正的数据页面之外,一般是针对于BLOB、TEXT这些大类型,行溢出是指字节溢出,而不是字符长度溢出,不过其实BLOB并不一定将数据放出行溢出,而且VARCHAR类型即使没有溢出也依然有可能被存放在行溢出数据

MySQL的VARCHAR类型可以存放 2 16 2^{16} 216字节,即65535字节,如果超过这个字节或者等于这个字节会变成TEXT类型(要改变SQL_MODE),比如下面的建表语句,但其实使用65532会报错的,因为字段本身还会有其他开销,所以只可以存65532字节

//注意这里一定要使用latin1的字符集,因为在这个字符集内,一个字符的最大占用字节为1
//所以varchar(65535)才会对应字节长度为65535,
//如果使用utf8的话,一个字符的最大占用字节为3,总最大的字节长度就是65535*3
CREATE TABLE i(
	a VARCHAR(65535)
)ENGINE=INNODB,CHARSET=latin1;

此外要注意的是,这个65535字节,并不是指单个varchar,而是指整个表非大字段类型的字段的bytes总和

但是问题来了?InnoDB存储引擎的页为16KB,也就是16384字节,是怎么存储65535字节的呢?

其实,在一般情况下,InnoDB存储引擎的数据都是存放在页的B-tree node中的(也就是索引的叶结点),但是如果发生行溢出的时候,就会存放在页类型为Uncompress BLOB页(未压缩的二进制页),而叶子结点里面对于该列只会存储前768字节数据,后面是偏移量,该偏移量是指向行溢出页的,其余的都在Uncompress BLOB页中

总体来说的结构会如下所示
在这里插入图片描述
下面就来详细说一下什么时候会发生行溢出

首先,我们要认识InnoDB存储引擎表是索引组织,即是一棵B+树,而B+树最低标准是基于2-3结点创造的,即每个结点都必须要有至少2个数据(对于InnoDB来说,子结点就是页,里面的数据就是行,所以每页里面都至少有2行)否则不可能形成树结构,而会形成链表,所以,InnoDB发生行溢出数据的条件,就是叶子结点无法存储至少2行数据
在这里插入图片描述
即并不是只要varchar或者text等类型,超过了768字节就会发生行溢出,只是如果发生了行溢出,那么varchar和text等类型,只会保留768字节,剩余的部分会放到BLOB PAGE里面。而发生行一处的条件就是一个页里面,存放不下两条行记录(书上说的是每一行的列的总字节长度为阈值为8098字节,即varchar(8098)【跟16KB的一半差不多】,即超过了这个字节,那么该页存储了这行就无法存储第二行,也就要发生行溢出)。所以,即使是BLOB和TEXT类型,也是要判断该页是否可以存放两行记录

那可不可能是其他类型的列发生行溢出呢?比如Int、BIGINT

这是不可能的,因为InnoDB对单表有列数的限定,最多只有1024个列,而类型里面占空间最大的为8位,即使1024个列都是最大位数,都不会超过(所以发生行数据溢出的,肯定是有变长类型或者TEXT和BLOB类型,虽然这里1024*8是大于8098的,是因为8098没有加上偏移量【指向溢出页的】)。

Compressed和Dynamic行记录模式

从MySQL5.7开始,默认的行记录格式为Dynamic格式,Compressed和Dynamic行记录格式称为Barracuda,而以前支持的Compact和Redundant称为Antelope文件格式。

这两种记录格式,如果发生了行溢出情况,对于存放在VARCHAR、VARBINARY、BLOB、TEXT中的数据采用了完全的行溢出的方式(即该行记录中最长的一列),在该行记录的存储列的部分,对于BLOB类型的,只会存放20个字节的指针,实际的数据都会在溢出页中,这里的溢出页称为Off Page,而之前的Compact和Redundant这两种格式会存放768个前缀字节。

唯一的不同是Compressed的行记录格式的另一个功能就是,存储在溢出页其中的行数据会进行压缩,使用zlib算法,因此对于BLOB、TEXT、VARCHAR这类大长度类型的数据能够进行非常有效的存储。不过这个是牺牲CPU来获取存储空间的,因为执行算法需要使用CPU
在这里插入图片描述

Dynamic、Compressed记录行的格式

Dynamic记录行的格式其实是跟Compact是一致的,除了使用了完全行溢出外,其他属性基本没变。

CREATE DATABASE hehe;
USE hehe;
CREATE TABLE a(
	a VARCHAR(4),
	b VARCHAR(5),
	c INT,
	d VARCHAR(6),
	e INT
)ENGINE=INNODB,CHARSET=utf8;
INSERT INTO a SELECT "aaa","bb",NULL,"c",4;

这是表的状态
在这里插入图片描述
同样,查看它的输出

hexdump -C a.ibd

在这里插入图片描述
可以看到01 02 03对应的是c、bb、aaa,然后04同样是NULL标志区,转换来就是0010,对应第三列为NULL。

所以可以推测Dynamic和Compressed的记录行的格式跟Compact是一样的,只不过在列部分内对于溢出行的处理方式不一样

CHAR的行结构存储

我们一般会认为VARCHAR是可变长的,而CHAR是不可变长的。

但实际上是,CHAR在InnoDB中也是被认为是变长字符类型,比如CHAR(10),10是指字符长度,然后utf8里面一个字符最大3个字节,所以CHAR(10)代表最大可以储存30字节的字符,但如果不够长度,后面依然是会进行自动填充的。

这也就意味着在Compact行记录里面的变长字段长度列表是可以看到CHAR类型列的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值