mysql InnoDB行记录格式

在介绍索引的文章已经知道。InnoDB的表数据被拆分成不同的数据页上,默认一个数据页大小是16kb,分布在聚簇索引的叶子节点上。被挂在B+树上。一条行记录除了要保存每列具体数据值还会有一些标识位信息。另外对于超长数据存储也有特殊处理。

那么具体到一行数据多个列数据是怎么组织的呢?InnoDB存储引擎提供4种行格式: REDUNDANT, COMPACT, DYNAMIC和COMPRESSED。

下图是4种行格式的对比:

Row Format 紧凑存储 加强可变长度存储 长索引key支持 是否支持压缩 支持表空间类型
REDUNDANT No No No No system, file-per-table, general
COMPACT Yes No No No system, file-per-table, general
DYNAMIC Yes Yes Yes No system, file-per-table, general
COMPRESSED Yes Yes Yes Yes file-per-table, general

这4种行格式其实后面的三种在数据格式上是一样的,只有REDUNDANT与其它三种格式不同。REDUNDANT是5.0之前的数据格式,已经过时了。从5.7之后版本默认行记录格式是DYNAMIC。

行数据的基本格式

行数据基本格式大致如下:

在这里插入图片描述

其中蓝色部分是行真正存储的数据区,是所有的列数据byte按顺序紧密连接在一起。黄色区域是非数据区,都是一些标识位信息。这一块几种行格式会有两种表现形式。

ROW_ID

如果没有主键,会有一个6字节长度的ROW_ID用来标识该列。

TRX_ID和ROLL_POINTER

每一行还有一个6字节的事务ID和7字节长度的回滚指针,这都是用来事务操作的。

头信息

头信息REDUNDANT 占用6个字节,其它占用5个字节。头信息中会有一些标识为,比如会存储下一行记录的地址信息。行是否被删除等等。

NULL列标识位

null列标识位REDUNDANT 格式是没有的,只有其它三种有。这个就是使用一个bit向量来标识每一列(这里的每一列是指的在表定义时候该列没有非空约束)是否为空,如果为NULL,则对应bit位为1。这部分字节长度由表中可空列的数量来决定,如小于8列则用一个字节长度就可以标识,如9~16长度则需要两个字节,依次类推这个也很好理解。

对于NULL的列,REDUNDANT 格式下如果列是可变长度不会在数据区进行存储,如果是定长列则会在蓝色数据区也占用相同的定长空间。而其它三种数据格式NULL值在数据区不占空间。

变长字段长度标识列表

这一部分是用来干什么的呢?想想前面的蓝色数据区是紧凑的字节连接在一起的,对于变长列数据长度又不是固定的,在数据查询抽取的时候怎么拆分每列数据呢?这里这部分就是用来标识每个变长列的字节长度的。只不过REDUNDANT和其它三种这里存储变长长度值格式又些不同。

REDUNDANT采用长度偏移量来表示。如果所有列长度小于127则每列长度使用一个字节来表示,否则使用两个字节。这里说的字节长度是每一列的长度偏移量都使用这么长的字节数。举个例子有三个列数据长度为10、20、30字节。长度总和为60则,每列长度偏移量使用1个字节就可以了。具体存储偏移量(0,10,30)。在header头部分有标识位标识使用1个字节还是2个字节。

其它三种,对于所有的非空可变长度列。如果列定义最大字节数不超过255字节(注意这里是列定义,也就是varchar(n)这样),则使用1个字节表示就可以了,因为一个字节整数最大值255。如果列定义允许最大长度超过255,这就需要分两种情况了,1、如果实际数据长度小于127字节,则用1个字节来表示其长度,2、若大于127字节则采用2字节来表示。这都是为了能省一个字节就省一个字节,要不然定义超过255直接两个字节长度就可以了。也没有必要过多去追究太细。

超长数据处理

InnoDB默认一个数据页大小是16kb,一个页上至少要存储2条记录。如果一行数据小于一个数据页的一半,则整行数据会存储在页内,如果超过了页的一半,可变长度列会被依次摘出存储在额外的空间(off-page)直到剩下行数据大小小于页的一般。这些单独分配的磁盘页称为溢出页(overflow pages)。这样的列称为页外列(off-page columns)。off-page columns的值存储在overflow pages的单链表中,每个这样的列都有自己的一个或多个overflow pages列表。对这4种行格式对于超长数据处理有一些不同

REDUNDANT和COMPACT 模式对于超长可变数据列,如果实际数据大小大于768字节,则在行数据上只会保留前768字节和对应溢出页的指针,超过的部分存储在溢出页。另外对于定长超过768同样按照这种方式处理。

DYNAMIC和COMPRESSED 模式对于实际数据大小超过768字节,则整个超长列数据都会存储在溢出页上,行记录上只保存一个到溢出页的指针。

长索引KEY

DYNAMIC和COMPRESSED两种模式还支持长索引key。也就是在一些超长字段上设置索引。索引key前缀最大长度只3072 字节。

配置行格式

默认行记录格式使用innodb_default_row_format变量来控制,如果在创建一个表的时候没有指定row_format,则默认使用ROW_FORMAT=DEFAULT来指定行记录格式。

查看当前默认行记录格式

SELECT @@innodb_default_row_format;

设置默认行记录格式

mysql> SET GLOBAL innodb_default_row_format=DYNAMIC;

可选值包括DYNAMIC、COMPACT和REDUNDANT。这里COMPRESSED不能被设置为默认行记录格式,因为COMPRESSED 模式不能使用在系统表空间上。

除了使用默认行记录格式,当然可以在创建表的时候显示的指定行记录格式

CREATE TABLE t1 (c1 INT) ROW_FORMAT=DYNAMIC;

查看当前表使用的行记录格式可以使用show table status like ‘表名’。

mysql> show table status like 'test'\G;
*************************** 1. row ***************************
           Name: test
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           ...

或者可以从INFORMATION_SCHEMA.INNODB_TABLES表中查看。

实例分析

准备表

CREATE TABLE test (
	id INT(11) NOT NULL AUTO_INCREMENT,
	a VARCHAR(10) NOT NULL ,
	b VARCHAR(30) NULL DEFAULT NULL ,
	c TEXT NULL DEFAULT NULL ,
	PRIMARY KEY (id) USING BTREE
)ENGINE=InnoDB

先插入一条记录

INSERT INTO test (a, b, c) VALUES ('a', 'b', null);
INSERT INTO test (a, b, c) VALUES ('a', 'bb', null);

找到表对应的数据文件test.ibd,使用editplus十六进制格式查看,查找62,因为’a’的ascii码也就是16进制格式是62。

80 00 00 02 00 00 00 00  93 EA B0 00 00 01 24 01
10 61 62 02 01 02 00 00  20 FF B5 80 00 00 02 00
00 00 00 93 EF 33 00 00  01 D1 02 41 61 62 62 00

这里很明显能看到 61 62 这些是实际数据ab,那从他这里往后应该就是第二行数据了。然后61 62 62这个是第二行的实际数据,那么这种中间的就是第二行的标识位信息。

结合第二行数据:2|a|bb|null

1、可变长度列表(逆序):02 01 (代表两个有值可变长度列长度第一个是1,第二个长度是2)

2、NULL标识位:02 。这里表最后两列是可空字段,然后一列有值一列没有值。02二进制格式是00000010。这里看到也是逆序标识的。

3、5字节的header头:00 00 20 FF B5

4、4字节的rowID(主键):80 00 00 02。这里主键设置int自增占4字节,实际值是2,不知道第一字节80代表什么。

5、剩下两部分TRX_ID和roll_pointer:00 00 00 00 93 EF 33 00 00 01 D1 02 41。

一个6字节一个7字节长度,共13字节

6、数据部分:61 62 62 (abb的ascii码)

你可能感兴趣的:(mysql学习记录,mysql,数据库)