InnoDB引擎行存储结构
文章目录
1.存储引擎
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7BY5kOU-1643188470321)(C:\Users\moozlee\Desktop\桌面文件夹\笔记\img\MySQL逻辑架构.jpg)]
上图展示了MySQL的逻辑架构,其中在Server端中真正与文件系统打交道的就是最核心的结构存储引擎,MySQL中存储引擎都被设计成可插拔式的,对于很多数据库基础的操作尽管存储引擎的实现可能不一样,但是这些对于上层模块是透明的,因此存储引擎可以说是MySQL的最为核心的模块,InnoDB存储引擎作为现在MySQL的主流以及默认存储引擎,我们将依次深入了解其记录的存储结构
2.InnoDB页的概念
和操作系统里的概念一样,由于内存和磁盘读写速度的差了10 ^ 3个数量级,因此对于从磁盘文件读入内存,以及从内存写入磁盘的操作需要以页的大小进行操作,InnoDB中页的大小一般是16KB,即一次读取最少从磁盘读入16KB的数据到内存,一次写入最少从内存写入16KB的数据到磁盘
MySQL中系统变量innodb_page_size表明了InnoDB存储引擎页的大小,默认为16834字节(16KB),该变量只能在第一次初始化MySQL数据目录指定,之后将无法更改
3.InnoDB行格式
MySQL向表中插入数据时是以行为单位,这些行在磁盘上存储形式成为行格式或者记录格式
目前,InnoDB引擎设计了4种不同类型的行格式
- COMPACT
- REDUNDANT
- DYNAMIC
- COMPRESSED
3.1 指定行格式
create table <表名> ( ... ) row_format=<行格式名称>;
alter table <表名> row_format=<行格式名称>;
3.2 COMPACT格式
变长字段长度列表 | NULL值列表 | 记录头信息 | 列1的值 | 列2的值 | … | 列n的值 |
---|
其中前三个为记录的额外信息,后面的是记录的真实数据
- 记录的额外信息
(1)变成字段长度列表
MySQL存在一些变长的数据类型,如varchar、varbinary等,这个字段存储的就是这些类型存储的字节数,这其实意味着,存储变长字段时会实际存储两部分数据,一部分是真正的数据内容,一部分是这些数据占用的字节数
这部分字节数会按照各变长字段列的顺序,逆序存储,原因后续会有说明
而对于这部分字节数到底采用少个字节存储,InnoDB有一套判定规则:
-
假设某个字符集种最多需要W各字节来表示一个字符,比如utf8mb4的W就是4,utf8字符集中的W就是3
-
对于变长类型VARCHAR(M)来说,这种类型表示能存储最多M个字符,因此这种类型表示的字符串最多占用字节数为M * W
-
假设实际存储的字符串占用的字节数是L
那么存在以下的规律:
- 如果 M * W <= 255,那么使用1字节来表示真实数据占用的字节数
- 如果 M * W > 255
- 如果 L <= 127,则用一个字节
- 如果 L > 127,则用两个字节
(2)NULL值列表
一条记录中,可能某些列存储NULL值,COMPACT为了节省空间会统一把这个记录中值为NULL的列集中管理
首先,对于那些不可能为NULL的列不会被列入管理的范围
然后,除开这些之外的可以存储NULL的列,每列都会对应一个二进制位,二进制位按照列的顺序逆序排列
- 二进制为1,代表列为NULL
- 二进制位0,代表列不为NULL
并且,MySQL规定NULL值列表必须使用整数个字节的位表示,因此,如果使用的二进制位不是整数个字节,则会在高位补0直至为整数个字节
(3)记录头信息
记录头信息固定5个字节,共40个二进制位,用于描述这一行记录的一些属性
名称 | 大小 | 描述 |
---|---|---|
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
deleted_flag | 1 | 标记该记录是否被删除 |
min_rec_flag | 1 | B+数的每层非叶子节点中最小的目录项记录都会添加这个标记 |
n_owned | 4 | 一个页面中的行会被分成若干个组,组中一个行的这个字段会记录该组中行的数目,其余行的这个字段为0 |
heap_no | 13 | 表示当前行在页面堆中的相对位置 |
revord_type | 3 | 表示当前行的类型,0表示普通记录,1表示B+数非叶子节点的目录项记录,2表示Infimum记录,3表示Supermum记录 |
next_record | 16 | 表示下一条记录的相对位置 |
- 记录的真实数据
除开我们每一行的各个字段外,MySQL会为每一行默认添加一些隐藏列
列名 | 是否必需 | 占用空间 | 描述 |
---|---|---|---|
db_row_id | 否 | 6字节 | 行ID,唯一标识一条记录 |
db_trx_id | 是 | 6字节 | 事务ID |
db_roll_ptr | 是 | 7字节 | 回滚指针 |
其中对于db_row_id,我们需要了解以下InnoDB的主键生成策略:优先使用用户自定义的主键为主键;如果用户没有定义主键,则选取UNIQUE键为主键;如果表中也没有UNIQUE修饰的列,则默认添加db_row_id的隐藏列为主键
3.3 REDUNDANT行格式
这个格式是MySQL5.0之前就在使用的一种行格式
字段长度偏移列表 | 记录头信息 | 列1的值 | 列2的值 | … | 列n的值 |
---|
- 字段长度偏移列表
对比于COMPACT格式的变长字段长度列表,存在两处不同:
- 没有了“变长”,说明会记录所有列的长度信息,按照逆序存储长度偏移列表
- 多了“偏移”。说明计算列值长度的方式是通过两个相邻偏移量的插值来计算各个列值的长度
- 记录头信息
REDUNDANT行格式的记录头信息占用6个字节,共48个二进制位
名称 | 大小 | 描述 |
---|---|---|
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
deleted_flag | 1 | 标记该记录是否被删除 |
min_rec_flag | 1 | B+数的每层非叶子节点中最小的目录项记录都会添加这个标记 |
n_owned | 4 | 一个页面中的行会被分成若干个组,组中一个行的这个字段会记录该组中行的数目,其余行的这个字段为0 |
heap_no | 13 | 表示当前行在页面堆中的相对位置 |
n_field | 10 | 记录表中列的数量 |
1byte_offs_flag | 1 | 标记字段长度偏移列表中梅格列对应的偏移量是使用1字节还是2字节表示 |
next_record | 16 | 表示下一条记录的相对位置 |
- NULL值的处理
由于没有NULL值列表,所以REDUNDANT会将列对应得偏移量值得第一个比特位作为是否位NULL得依据,该比特位称之为NULL比特位
3.4 溢出列
之前提到过,InnoDB以页为单位管理存储空间,每一行都会被分配到某个页中存储,一个页大小为16K,但是当某个记录所要存储的空间大于16K时,就不能使用一个页存下所有数据
处理方法也和操作系统内存管理很相似,在COMPACT和REDUNDANT两种格式中,会存储768字节的数据,然后再使用20个字节存储指向存储剩余数据的其他页的地址
3.5DYNAMIC行格式和COMPRESSED行格式
MySQL5.7的默认行格式就是DYNAMIC,这两个行格式和COMPACT格式类似,只不过在处理溢出列数据的时候会有不同,DYNAMIC并不会存储前768个字节,而是将所有数据都存储到溢出页中;而COMPRESSED不同于DYNAMIC的一点是会使用压缩算法对页面进行压缩