InnoDb:

InnoDB将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

COMPACT行格式

image_1c9g4t114n0j1gkro2r1h8h1d1t16.png-42.4kB

从图中可以看出来,一条完整的记录其实可以被分为记录的额外信息记录的真实数据两大部分

变长字段长度列表

MySQL支持一些变长的数据类型,比如VARCHAR(M)VARBINARY(M)、各种TEXT类型,各种BLOB类型,我们也可以把拥有这些数据类型的列称为变长字段,变长字段中存储多少字节的数据是不固定的,所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来,这样才不至于把MySQL服务器搞懵,所以这些变长字段占用的存储空间分为两部分:

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

Compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放,我们再次强调一遍,是逆序存放!

变长字段长度列表中只存储值为 非NULL 的列内容占用的长度,值为 NULL 的列的长度是不储存的 。

NULL值列表

我们知道表中的某些列可能存储NULL值,如果把这些NULL值都放到记录的真实数据中存储会很占地方,所以Compact行格式把这些值为NULL的列统一管理起来,存储到NULL值列表中,它的处理过程是这样的:

首先统计表中允许存储NULL的列有哪些。

如果表中没有允许存储 NULL 的列,则 NULL值列表 也不存在了,否则将每个允许存储NULL的列对应一个二进制位,二进制位按照列的顺序逆序排列,二进制位表示的意义如下:

  • 二进制位的值为1时,代表该列的值为NULL
  • 二进制位的值为0时,代表该列的值不为NULL

MySQL规定NULL值列表必须用整数个字节的位表示,如果使用的二进制位个数不是整数个字节,则在字节的高位补0

记录头信息

它是由固定的5个字节组成。5个字节也就是40个二进制位,不同的位代表不同的意思,如图:

image_1c9geiglj1ah31meo80ci8n1eli8f.png-29.5kB

记录的真实数据

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

总结

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

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

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

    ALTER TABLE 表名 ROW_FORMAT=行格式名称
  3. InnoDB目前定义了4种行格式

    • COMPACT行格式

      具体组成如图:

      image_1c9g4t114n0j1gkro2r1h8h1d1t16.png-42.4kB

    • Redundant行格式

      具体组成如图:

      image_1ctfppb4c1cng1m8718l91760jde9.png-36.2kB

    • Dynamic和Compressed行格式

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

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

  • 一个页一般是16KB,当记录中的数据太多,当前页放不下的时候,会把多余的数据存储到其他页中,这种现象称为行溢出

InnoDB数据页结构

我们表中记录的那种类型的页,官方称这种存放记录的页为索引(INDEX)页.鉴于我们还没有了解过索引是个什么东西,而这些表中的记录就是我们日常口中所称的数据,所以目前还是叫这种存放记录的页为数据页吧。

数据页代表的这块16KB大小的存储空间可以被划分为多个部分,不同部分有不同的功能,各个部分如图所示:

img

image-20200807104104054

记录在页中的存储

总结

  1. InnoDB为了不同的目的而设计了不同类型的页,我们把用于存放记录的页叫做数据页
  2. 一个数据页可以被大致划分为7个部分,分别是
    • File Header,表示页的一些通用信息,占固定的38字节。
    • Page Header,表示数据页专有的一些信息,占固定的56个字节。
    • Infimum + Supremum,两个虚拟的伪记录,分别表示页中的最小和最大记录,占固定的26个字节。
    • User Records:真实存储我们插入的记录的部分,大小不固定。
    • Free Space:页中尚未使用的部分,大小不确定。
    • Page Directory:页中的某些记录相对位置,也就是各个槽在页面中的地址偏移量,大小不固定,插入的记录越多,这个部分占用的空间越多。
    • File Trailer:用于检验页是否完整的部分,占用固定的8个字节。
  3. 每个记录的头信息中都有一个next_record属性,从而使页中的所有记录串联成一个单链表
  4. InnoDB会把页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个,存放在Page Directory中,所以在一个页中根据主键查找记录是非常快的,分为两步:
    • 通过二分法确定该记录所在的槽。
    • 通过记录的next_record属性遍历该槽所在的组中的各个记录。
  5. 每个数据页的File Header部分都有上一个和下一个页的编号,所以所有的数据页会组成一个双链表
  6. 为保证从内存中同步到磁盘的页的完整性,在页的首部和尾部都会存储页中数据的校验和和页面最后修改时对应的LSN值,如果首部和尾部的校验和和LSN值校验不成功的话,就说明同步过程出现了问题。