InnoDB中磁盘和内存之间交互的基本单位是,默认大小为16KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。目前共4种:

  • Compact
  • Redundant
  • Dynamic(5.7默认)
  • Compressed

COMPACT行格式

一条完整的记录其实可以被分为记录的额外信息记录的真实数据两大部分。

记录的额外信息

这部分信息是服务器为了描述这条记录而不得不额外添加的一些信息,这些额外信息分为3类,分别是变长字段长度列表NULL值列表记录头信息

变长字段长度列表

在COMPACT行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放。
内容占用的字节数比较小,用1个字节就可以表示,但是如果变长列的内容占用的字节数比较多,可能就需要用2个字节来表示。如果该可变字段允许存储的最大字节数超过255字节并且真实存储的字节数超过127字节,则使用2个字节,否则使用1个字节。
变长字段长度列表中只存储值为非NULL的列内容占用的长度,值为NULL的列的长度是不储存的。
变长字段长度列表不是必须的,如果表中所有的列都不是变长的数据类型的话,这一部分就不需要有。

NULL值列表

我们知道表中的某些列可能存储NULL值,如果把这些NULL值都放到记录的真实数据中存储会很占地方,所以Compact行格式把这些值为NULL的列统一管理起来,存储到NULL值列表中。
如果表中没有允许存储NULL的列,则NULL值列表也不存在了,否则将每个允许存储NULL的列对应一个二进制位(1代表为NULL,0代表非NULL),二进制位按照列的顺序逆序排列。

逆序

变长字段长度列表、NULL值列表中的信息都是逆序存放,这样可以使记录中位置靠前的字段和它们对应的字段长度信息在内存中的距离更近,可能会提高高速缓存(操作系统内核空间的缓冲区)的命中率。

记录头信息

由固定的5个字节组成。5个字节也就是40个二进制位,不同的位代表不同的意思。主要用于储存记录的元信息,例如标记记录是否删除、表示下一条记录的相对位置等等。

记录的真实数据

记录的真实数据除了我们自己定义的列的数据以外,MySQL会为每个记录默认的添加一些列(也称为隐藏列)。

列名 是否必须 占用空间 描述
DB_ROW_ID 6字节 行ID,唯一标识一条记录
DB_TRX_ID 6字节 事务ID
DB_ROLL_PTR 7字节 回滚指针

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

Redundant行格式

MySQL5.0之前用的一种行格式,不表了。

行溢出数据

VARCHAR(M)最多能存储的数据

我们知道对于VARCHAR(M)类型的列最多可以占用65535个字节。其中的M代表该类型最多存储的字符数量。如果我们使用ascii字符集的话,一个字符就代表一个字节。但是不能真的存下65535个字节。因为,这个65535个字节除了列本身的数据之外,还包括一些其他的数据(storage overhead)。为了存储一个VARCHAR(M)类型的列,其实需要占用3部分存储空间:

  • 真实数据
  • 真实数据占用字节的长度
  • NULL值标识,如果该列有NOT NULL属性则可以没有这部分存储空间

注意!一个行中的所有列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过65535个字节!所以这一小节的内容是以一个表只有一列为前提。

记录中的数据太多产生的溢出

前面提到,Innodb是以页为基本单位来管理存储空间的,一个页的大小一般是16KB,也就是16384字节。而一行最多可以存储65532个字节,这样就可能造成一个页存放不了一条记录的尴尬情况。
在Compact和Redundant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址(当然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数),从而可以找到剩余数据所在的页。
对于Compact和Redundant行格式来说,如果某一列中的数据非常多的话,在本记录的真实数据处只会存储该列的前768个字节的数据和一个指向其他页的地址,然后把剩下的数据存放到其他页中,这个过程也叫做行溢出,存储超出768字节的那些页面也被称为溢出页

行溢出的临界点

MySQL中规定一个页中至少存放两行记录。只要不满足这个规定,那么就会发生行溢出。

Dynamic和Compressed行格式

这俩行格式和Compact行格式挺像,只不过在处理行溢出数据时有点儿分歧,它们不会在记录的真实数据处存储字段真实数据的前768个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据处存储其他页面的地址。

Compressed行格式和Dynamic不同的一点是,Compressed行格式会采用压缩算法对页面进行压缩,以节省空间。