1.1.1. 03.InnoDB 页的简介

简介

InnoDB 是一个持久化的存储引擎。数据存储在磁盘上,而真正的处理数据是在内存中进行的(内存处理速度快)。

InnoDB 会将数据划分成若干个页,以页作为磁盘和内存之间交互的「基本单位」,一般大小为 16KB。所以不管是从磁盘读取还是从内存写入磁盘,最少都会取 16KB 的内容。

InnoDB 行格式

InnoDB 把记录在磁盘上的存取方式称之为「行格式」或者「记录格式」。目前有四种「行格式」:「Compact」、「Redundant」、「Dynamic」、「Compressed」。他们大致原理相同,只有少许差别。

指定行格式的语法

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

ALTER TABLE 表名 ROW_FORMAT=行格式名称

先创建一个演示表

mysql> CREATE TABLE record_format_demo (
    ->     c1 VARCHAR(10),
    ->     c2 VARCHAR(10) NOT NULL,
    ->     c3 CHAR(10),
    ->     c4 VARCHAR(10)
    -> ) CHARSET=ascii ROW_FORMAT=COMPACT;
Query OK, 0 rows affected (0.03 sec)

# 插入数据后
mysql> SELECT * FROM record_format_demo;
+------+-----+------+------+
| c1   | c2  | c3   | c4   |
+------+-----+------+------+
| aaaa | bbb | cc   | d    |
| eeee | fff | NULL | NULL |
+------+-----+------+------+
2 rows in set (0.00 sec)
compact 行格式

Compact

一条记录其实被分为「记录的额外信息」和「记录的真实信息」 额外信息分为「变长字段长度列表」、「NULL 值列表」、「记录头信息」

变长字段长度列表:

如 VARCHAR(M)、VARBINARY(M)、各种 TEXT 类型,各种 BLOB 类型 都是「变长字段」,存储了多少字节的数据是不固定的,所以 mysql 要记录真实数据占用的字节数,才不会让 mysql 服务器懵,所以「变长字段长度列表」分为两部分:

  1. 真正的数据内容
  2. 占用的字节数

在 Compact 行格式中,所有「变长字段」的长度排列在一起形成一个变长字段长度列表放在记录开头,并且个字段数据占用的字节数按照 逆序 存放。注意是「逆序」。

拿上面的第一条记录当例子来看。

列名 存储内容 内容长度(十进制表示) 内容长度(十六进制表示)
c1 'aaaa' 4 0x04
c2 'bbb' 3 0x03
c4 'd' 1 0x01

所以实际样子是: 第一条记录的存储格式

由于 c1、c2、c4 列的字符串都比较短,所以用一个字节表示就行了。但是如果字符较长,也有可能采用 2 个字节表示。 首先声明下:

  1. W:一个字节最多需要使用的字节数,也就是 show charset 语句中的 maxlen 列。如 utf8 是 3,gbk 是 2,ascii 是 1。
  2. M:这个字节最多存储 M 个「字符」。如 VARCHAR(M),这个最多能表示的字节数是 M*W。
  3. L:实际存储锁占用的字节数。

规则是这样的:

  • 如果 M*W <= 255,那么使用 1 个字节来表示真正占用的字节数。
  • 如果 M*W > 255,则分为两种情况
    • 如果 L<=127 ,则用 1 个字节表示真正字符串占用的字节数。
    • 如果 L>127,则用 2 个字节表示真正字符串占用的字节数。

总结一下就是说:如果该可变字段允许存储的最大字节数(M×W)超过255字节并且真实存储的字节数(L)超过127字节,则使用2个字节,否则使用1个字节。

注意:变长字段长度列表对于值为 NULL 的列长度是不存储的。

1

NULL 列表

顾名思义,把某些列存 NULL 值的都放在 NULL 值列表中。可以让「记录的真实数据」中存储释放空间。 处理过程:

  1. 首先统计哪些列可以存储 NULL 。如例子中可以看到 C1、C3、C4 都可以存储 NULL 值。C2 不允许。
  2. 如果表中没有允许存储 NULL 的列,那么 NULL 值列表也就不存在了。否则,每个存储 NULL 的列对应一个二进制位逆序排列。
    • 1 表示该值是 NULL。
    • 0 表示该值不为 NULL。
  3. Mysql 规定 NULL 值列表必须用整数个字节的位表示,如果不够则高位补 0。下图为当前例子的示意图,加入有 9 个允许为 NULL,则用 2 个字节来表示。

那么第一条记录就是: 1

而第二条记录是:

1

所以在填充了 NULL 值列表后,示意图是这样的: 1

记录头信息

1

名称 大小(单位: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 表示下一条记录的相对位置

头信息先暂时这样,等很后面遇到后再详细展开。

记录的真实信息

除了定义的列之外,Mysql 还记录了一些隐藏列(由 Mysql 生成):

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

实际上这几个列的真正名称其实是:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR,我们为了美观才写成了row_id、transaction_id和roll_pointer。

InnoDB 对主键的生成策略:优先使用用户自定义的主键作为主键,如果没有定义主键,则选取一个 Unique 键作为主键,如果没有 Unique 键,则 InnoDB 会为表默认添加一个名为 row_id 的隐藏列作为主键。

1

Redundant 行格式

1

字段长度偏移列表

该列会记录所有列(包括隐藏列)的长度,都会「逆序」存储到「字段长度偏移列表」。 有「偏移」这个字眼,那么计算列值长度就没有那么直观。 如第一条的「字段长度偏移列表」是

25 24 1A 17 13 0C 06

逆序存放之后就是

25 24 1A 17 13 0C 06

其意思是:

第一列(`row_id`)的长度就是 0x06个字节,也就是6个字节。

第二列(`transaction_id`)的长度就是 (0x0C - 0x06)个字节,也就是6个字节。

第三列(`roll_pointer`)的长度就是 (0x13 - 0x0C)个字节,也就是7个字节。

第四列(`c1`)的长度就是 (0x17 - 0x13)个字节,也就是4个字节。

第五列(`c2`)的长度就是 (0x1A - 0x17)个字节,也就是3个字节。

第六列(`c3`)的长度就是 (0x24 - 0x1A)个字节,也就是10个字节。

第七列(`c4`)的长度就是 (0x25 - 0x24)个字节,也就是1个字节。
Dynamic 和 Compressed 行格式

这两种行格式类似于 COMPACT 行格式。只是在处理行溢出数据时不同,它们不会在记录的真实数据处存储字符串的前768个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据处存储其他页面的地址。 Compressed 换格式还会采用压缩算法对页面进行压缩。

Copyright © Kagami丶 2019 all right reserved,powered by Gitbook该文件修订时间: 2019-12-10 20:25:25

results matching ""

    No results matching ""