MySQL之InnoDB数据存储结构

一、概述

二、行

1、简介

我们平时的数据以行为单位来向表中插入数据,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。InnoDB存储引擎设计了4种不同类型的行格式,分别是CompactRedundantDynamicCompressed行格式。

查看MySQL8的默认行格式:

mysql> SELECT @@innodb_default_row_format;

2、Compact行格式

变长字段长度列表

MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、TEXT类型,BLOB类型,这些数据类型修饰列称为变长字段,变长字段中存储多少字节的数据不是固定的,所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来。在Compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表。

② NULL值列表

Compact行格式会把可以为NULL的列统一管理起来,存在一个标记为NULL值列表中。如果表中没有允许存储 NULL 的列,则 NULL值列表也不存在了。

1. 二进制位的值为1时,代表该列的值为NULL。

2. 二进制位的值为0时,代表该列的值不为NULL。

③ 记录头信息

这些记录头信息中各个属性如下:

3、Dynamic和Compressed行格式

① Dynamic

在MySQL 8.0中,默认行格式就是Dynamic

DynamicCompressed行格式和Compact行格式相同,但是在处理行溢出数据时有区别

  • CompressedDynamic两种记录格式对于存放在BLOB中的数据采用了完全的行溢出的方式。

  • CompactRedundant两种格式会在记录的真实数据处存储一部分数据(存放768个前缀字节)。

② Compressed

Compressed行记录格式的另一个功能就是,存储在其中的行数据会以zlib的算法进行压缩,因此对于BLOB、TEXT、VARCHAR这类大长度类型的数据能够进行非常有效的存储。

4、Redundant行格式

Redundant行格式的首部是一个字段长度偏移列表,同样是按照列的顺序逆序放置的

三、页

1、简介

InnoDB将数据划分为若干个页, InnoDB中页的大小默认为16KB。

  • 可通过show variables like '%innodb_page_size%';查看默认页的大小

  • 页作为磁盘和内存之间交互的基本单位,也就是一次最少从磁盘中读取16KB的内容到内存中。

  • 在数据库中,不论读一行,还是读多行,都是将这些行所在的页进行加载。

  • 数据库管理存储空间的基本单位是页(Page),数据库I/o操作的最小单位是页。一个页中可以存储多个行记录。

2、构成

File Header(文件头部):

描述各种页的通用信息。(比如页的编号、其上一页、下一页是谁等)大小:38字节

Page Header(页面头部)

为了能得到一个数据页中存储的记录的状态信息,比如本页中已经存储了多少条记录,第一条记录的地址是什么,页目录中存储了多少个槽等等,特意在页中定义了一个叫Page Header的部分,这个部分占用固定的56个字节,专门存储各种状态信息。

③ Infimum + Supremum(最小最大记录)

InnoDB规定的最小记录与最大记录这两条记录的构造十分简单,都是由5字节大小的记录头信息和8字节大小的一个固定的部分组成的

User Records (用户记录)

User Records中的这些记录按照指定的行格式一条一条摆在User Records部分,相互之间形成单链表

⑤ Free Space (空闲空间)

我们自己存储的记录会按照指定的行格式存储到User Records部分。但是在一开始生成页的时候,其实并没有User Records这个部分,每当我们插入一条记录,都会从Free Space部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。

Page Directory(页目录)

在页中,记录是以单向链表的形式进行存储的。单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索。因此在页结构中专门设计了页目录这个模块,专门给记录做一个目录,通过二分查找法的方式进行检索,提升效率。

File Trailer(文件尾部)

前4个字节代表页的校验和:这个部分是和File Header中的校验和相对应的。

后4个字节代表页面被最后修改时对应的日志序列位置(LSN):

这个部分也是为了校验页的完整性的,如果首部和尾部的LSN值校验不成功的话,就说明同步过程出现了问题。

四、区

1、特点

  • 一个区就是在物理位置上连续的64个页。

  • 因为InnoDB 中的页大小默认是16KB,所以一个区的大小是64*16KB= 1MB。

  • 在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区为单位分配,甚至在表中的数据特别多的时候,可以一次性分配多个连续的区。

  • 虽然可能造成一点点空间的浪费(数据不足以填充满整个区),但是从性能角度看,可以消除很多的随机I/O,功大于过!

2、区分类

空闲的区(FREE):现在还没有用到这个区中的任何页面。

有剩余空间的碎片区(FREE_FRAG):表示碎片区中还有可用的页面。

没有剩余空间的碎片区(FULL_FRAG):表示碎片区中的所有页面都被使用,没有空闲页面。

附属于某个段的区(FSEG):每一个索引都可以分为叶子节点段和非叶子节点段。

五、段

InnoDBB+树叶子节点非叶子节点进行了区别对待,也就是说叶子节点自己独有的区非叶子节点有自己独有的区

存放叶子节点的区的集合就算是一个段( segment),存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成2个段,一个叶子节点段,一个非叶子节点段

InnoDB中还有为存储一些特殊的数据而定义的段,比如回滚段。所以,常见的段有数据段索引段回滚段

  • 数据段即为B+树的叶子节点索引段即为B+树的非叶子节点

在InnoDB存储引擎中,对段的管理都是由引擎自身所完成,DBA不能也没有必要对其进行控制。这从一定程度上简化了DBA对于段的管理。段其实不对应表空间中某一个连续的物理区域,而是一个逻辑上的概念,由若干个零散的页面以及一些完整的区组成。

六、表空间

表空间是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。

表空间数据库由一个或多个表空间组成,表空间从管理上可以划分为

  • 系统表空间(System tablespace):数据信息索引信息都存储在/var/lib/mysql/ibdata1

  • 独立表空间(File-per-table tablespace):数据信息索引信息都存储在/var/lib/mysql/数据库名/表名.ibd

  • 撤销表空间(Undo Tablespace)

  • 临时表空间(Temporary Tablespace)

  • ......等

1、系统表空间

系统表空间中会额外记录一些有关整个系统信息的页面,整个MySQL进程只有一个系统表空间

2、独立表空间

独立表空间,即每张表有一个独立的表空间,也就是数据和索引信息都会保存在自己的表空间中独立的表空间(即:单表)可以在不同的数据库之间进行迁移。

我们到数据目录里看,会发现一个新建的表对应的.ibd文件只占用了96K,才6个页面大小(MySQL5.7中)),这是因为一开始表空间占用的空间很小,因为表里边都没有数据。不过别忘了这些.ibd文件是自扩展的,随着表中数据的增多,表空间对应的文件也逐渐增大。

查看InnoDB的表空间类型:

mysql > show variables like 'innodb_file_per_table';