MySQL(一)InnoDB 记录存储结构

InnoDB 记录存储结构

  1. 页是MySQL中磁盘和内存交互的基本单位,也是MySQL是管理存储空间的基本单位。

  2. 指定和修改行格式的语法如下:

    CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称
    
    ALTER TABLE 表名 ROW_FORMAT=行格式名称
    
  3. InnoDB目前定义了4种行格式

    • COMPACT行格式

      具体组成如图:

      • 变长字段长度列表存放的是每个变长字段存储的字节数,通过字符数*每个字符占用的字节来记录
      • null值列表存的是该条记录哪几列为null(逆序),比如第三第四列为空,主键列NOT NULL,第二列标记为NOTNULL,则这个标志位为000001(c4)1(c3)0(0x06)
  • Redundant行格式

    具体组成如图:

  • Dynamic和Compressed行格式

    这两种行格式类似于COMPACT行格式,只不过在处理行溢出数据时有点儿分歧,它们不会在记录的真实数据处存储字符串的前768个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据处存储其他页面的地址。

    另外,Compressed行格式会采用压缩算法对页面进行压缩。

  • 一个页一般是16KB,当记录中的数据太多,当前页放不下的时候,会把多余的数据存储到其他页中,这种现象称为行溢出。如果某个字段长度大于了16KB,该记录在单个页面中无法存储时,InnoDB会把一部分数据存放到所谓的溢出页中。记录的真实数据处只会存储该列的前768个字节的数据和一个指向其他页的地址,然后把剩下的数据存放到其他页中,这个过程也叫做行溢出,存储超出768字节的那些页面也被称为溢出页

每一条数据的结构:

这些二进制位代表的详细信息如下表:

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

一个InnoDB数据页的存储空间大致被划分成了7个部分,有的部分占用的字节数是确定的,有的部分占用的字节数是不确定的。

名称 中文名 占用空间大小 简单描述
File Header 文件头部 38字节 页的一些通用信息
Page Header 页面头部 56字节 数据页专有的一些信息
Infimum + Supremum 最小记录和最大记录 26字节 两个虚拟的行记录
User Records 用户记录 不确定 实际存储的行记录内容
Free Space 空闲空间 不确定 页中尚未使用的空间
Page Directory 页面目录 不确定 页中的某些记录的相对位置
File Trailer 文件尾部 8字节 校验页是否完整

记录在页中的存储

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

被删除的记录只是会标记为被删除,而并不会从磁盘中删除调,因为重新排列需要性能消耗。而会在新的记录插入的时候(相同id)占用到被删除的记录的空间(可重用空间)

每个页中的数据会被划分成几个组,每个组的最后一条记录的头信息中的n_owned属性表示该记录拥有多少条记录也就是这个组有多少条记录,且把他的地址偏移量单独提取出来按顺序存储到靠近页的尾部的地方(页目录,也就是每个组最后一条记录的地址偏移量存到页目录的每个槽中),页目录中的这些偏移量被称为槽。

最小记录和最大记录分别是头尾节点,并不是真实的数据记录。在一个组中的记录数等于8个后再插入一条记录时,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个来记录这个新增分组中最大的那条记录的偏移量。

InnoDB可能不可以一次性为这么多数据分配一个非常大的存储空间,如果分散到多个不连续的页中存储的话需要把这些页关联起来,FIL_PAGE_PREVFIL_PAGE_NEXT就分别代表本页的上一个和下一个页的页号。这样通过建立一个双向链表把许许多多的页就都串联起来了,而无需这些页在物理上真正连着。需要注意的是,并不是所有类型的页都有上一个和下一个页的属性,不过我们本集中唠叨的数据页(也就是类型为FIL_PAGE_INDEX的页)是有这两个属性的,所以所有的数据页其实是一个双链表,就像这样

InnoDB存储引擎会把数据存储到磁盘上,但是磁盘速度太慢,需要以为单位把数据加载到内存中处理,如果该页中的数据在内存中被修改了,那么在修改后的某个时间需要把数据同步到磁盘中。

单页内查询:

根据主键查找的时候,用的是二分法,先计算中间槽的位置对应记录的主键值(这个槽的最后一条记录),用二分法找到所在的槽,然后找到该槽所在分组中主键最小的那条记录(通过找到上一个槽对应的记录的下一个节点),从那条记录开始遍历该槽所在的组中的各个记录。

多页查询:

1、定位记录所在的页(如何定位到页,就需要用到索引了)

2、在该页用单页的方法查询

你可能感兴趣的:(MySQL(一)InnoDB 记录存储结构)