目录

一、基本存储结构-页

二、页的上层结构

三、页的内部结构

 3.1 文件头与文件尾

3.2 记录部分

3.3 页头与页目录

 四、记录的行格式

4.1 Compact行格式

 4.1.1 变长字段长度列表

4.1.2 NULL值列表

4.1.3 记录头信息

 4.1.4 真实信息

4.2 Dynamic和Compressed行格式

 4.3 Redundant 行格式(5.0之前的格式,略)

 五、区/段/碎片区

5.1 为什么引入区?

5.2 为什么引入段?

5.3 为什么引入碎片区?


一、基本存储结构-页

,每个页默认大小16KB

        页是磁盘内存交互的基本单位,每次IO最少读取16KB的内容到内存。也就是说,IO基本单位。一个页中可以存储多个行记录

        页之间可以不在物理结构上相连,通过双向链表相关联。页内的记录主键大小排序构成单向链表

B Mysql 树存储 mysql数据存储结构_B Mysql 树存储

二、页的上层结构

B Mysql 树存储 mysql数据存储结构_表空间_02

行->页->区->段->表空间 

        在文件系统中是连续分配的空间,一个区等于64个页,大小:64*16K=1MB

       中不要求区之间相邻的,不同类型数据库对象以不同段的形式存在。例如数据表段索引段

        表空间可存储一个或多个,数据库由多个表空间组成,空间可划分为系统表空间(只有一个,额外记录系统信息)、用户表空间、临时表空间等。

三、页的内部结构

B Mysql 树存储 mysql数据存储结构_B Mysql 树存储_03

B Mysql 树存储 mysql数据存储结构_mysql_04

 3.1 文件头与文件尾

         文件头(38字节)的作用

通用信息。(比如页的编号类型、校验和、上一页下一页,页面被最后修改时对应的日志序列位置

        文件尾(8字节)的作用

                前4个字节代表校验和:这部分和文件头中的校验和对应
 
                后4个字节代表页面被最后修改时对应的日志序列位置(LSN):这个部分也是为了校验页的完整性的,如果首部和尾部的LSN值校验不成功的话,就说明同步过程出现了问题。

3.2 记录部分

        页的主要作用是存储记录,所以“最大最小记录”和“用户记录”部分占了页结构的主要空间
        

B Mysql 树存储 mysql数据存储结构_mysql_05

         最开始没有用户记录,全是空闲空间。我们每加一条记录,就会把空闲空间分出来一部分给用户记录,用户记录之间主键大小单链表连接。如果空闲空间用完了,就申请新的页。

3.3 页头与页目录

        页头

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

        页目录

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

B Mysql 树存储 mysql数据存储结构_B Mysql 树存储_06

 四、记录的行格式

4.1 Compact行格式

        在MySQL 5.1版本中,默认设置为Compact行格式。一条完整的记录其实可以被分为记录的额外信息和记录的真实数据两大部分。

B Mysql 树存储 mysql数据存储结构_数据库_07

 4.1.1 变长字段长度列表

        MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、TEXT类型,BLOB类型,这些数据类型修饰列称为变长字段,变长字段中存储多少字节的数据不是固定的,所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来。

4.1.2 NULL值列表

         Compact行格式会把可以为NULL的列统一管理起来,存在一个标记为NULL值列表中。如果表中没有允许存储 NULL 的列,则 NULL值列表也不存在了。
        之所以要存储NULL是因为数据都是需要对齐的,如果没有标注出来NULL值的位置,就有可能在查询数据的时候出现混乱

4.1.3 记录头信息

        包含如下属性。

B Mysql 树存储 mysql数据存储结构_数据_08

 4.1.4 真实信息

        除了真正的信息,还有三个隐藏列:

行ID:没有指定主键和Unique时存在,唯一标识一条记录

事务ID

回滚指针

4.2 Dynamic和Compressed行格式

        一个的大小一般是16KB,也就是16384字节,而一个VARCHAR(M)类型的列就最多可以存储65533个字节,这样就可能出现一个存放不了一条记录,这种现象称为行溢出。 

        在MySQL 8.0中,默认行格式就是Dynamic,Dynamic、Compressed行格式和Compact行格式挺像,只不过在处理行溢出数据时有分歧:
 
        Compressed和Dynamic两种记录格式对于存放在BLOB中的数据采用了完全行溢出的方式。如图,在数据页中只存放20个字节的指针溢出页地址),实际的数据都存放在溢出页中。

B Mysql 树存储 mysql数据存储结构_mysql_09

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

B Mysql 树存储 mysql数据存储结构_mysql_10

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

 4.3 Redundant 行格式(5.0之前的格式,略)

 五、区/段/碎片区

5.1 为什么引入区?

        我们知道页之间用双向链表相连,但页之间的物理位置可能相距很远。假如进行范围查询,我们顺着双向链表去查找,相当于随机IO,速度非常慢。为了尽可能让页按顺序存储,读取时采用顺序IO,我们引入了的概念:一个区就是物理位置连续的64个。表中数据量大时,为索引分配空间就以为单位分配,而非

5.2 为什么引入段?

范围查询,就是对B+树叶子节点记录扫描。如果不区分叶子节点非叶子节点,统统放进同一个区的话,查询效果就会降低。所以我们把叶子节点和非叶子节点分开,放到不同。那么相同类型就形成了。例如一个索引会生成数据段索引段等。

5.3 为什么引入碎片区?

        一个默认占用1M的空间,一个索引生成两个,而段以分配空间。但记录较少的时候我们没必要申请这么多空间,所以就引入了碎片区,碎片区中的所属的段不确定碎片区直属于表空间,不属于任何一个

        刚开始插数据时,是从某个碎片区单个页面分配存储空间的,直到某个占用了32个碎片区页面后才会申请完整的区单位分配空间

        所以可分为四类

空闲区  2.有剩余空间的碎片区  3.无剩余空间的碎片区  4.属于某个段的区