InnoDB页结构
页(Page)是 Innodb 存储引擎用于管理数据的最小磁盘单位。常见的页类型有数据页、Undo 页、系统页、事务数据页等,本文主要分析的是数据页。
InnoDb中的页大小一般为16k—操作系统页(4k)的倍数
查询一页的大小 (16384B):
页的结构:
InnoDB行格式
一行记录可以以不同的格式存在InnoDB中,行格式分别是Compact、Redundant、Dynamic和Compressed行格式。
我们可以在创建或修改表的语句中指定行格式:
CREATE TABLE 表名(列的信息) ROW_FORMAT=行格式名称
ALTER TABLE 表名 ROW_FORMAT=行格式名称
Compact 行格式
变长字段长度列表:逆序记录每一个列实际数据的长度(按列的先后顺序进行排序),如果列的长度小于 255 字节,则使用一个字节,否则使用 2 个字节。该字段的实际长度取决于列数和每一列的长度,因此是变长的。
NULL 标志位:至少一个字节,表示该行是否有 NULL 值,1表示null,0表示不为null NULL标志位只标识没有声明not null的列
记录头信息:五个字节,其中 next_record 记录了下一条记录的相对位置,一个页中的所有记录使用这个字段形成了一条单链表。
列数据部分:除了记录每一列对应的数据外,还有隐藏列,它们分别是 Transaction ID、Roll Pointer 以及 row_id(当没有指定主键)。
记录头信息40bit(5个字节)的意义
记录的真实数据除了我们自己定义的列的数据以外,还会有三个隐藏列
(实际上这几个列的真正名称其实是:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR):
一个表没有手动定义主键,则会选取一个Unique键作为主键,如果连Unique键都没有定义的话,则会为表默
认添加一个名为row_id的隐藏列作为主键。所以row_id是在没有自定义主键以及Unique键的情况下才会存在
的。这也是InnoDB可以为每个表创建B+Tree的原因
行溢出
变长数据会行溢出,例varchar(M)。
VARCHAR(M)类型的列最多可以占用65535个字节。其中的M代表该类型最多存储的字符数量,如果我们使用
ascii字符集的话,一个字符就代表一个字节。
VARCHAR(65535)不可用:
MySQL对一条记录占用的最大存储空间是有限制的,除BLOB或者TEXT类型的列之外,
其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过65535个字节。
这个65535个字节除了列本身的数据之外,还包括一些其他的数据,比如说我们为了存储一个VARCHAR(M)类型的列,其实需要占用3部分存储空间:
- 真实数据
- 变长字段真实数据的长度(2字节)
- NULL值标识(不声明not null时)(1字节)
c1 varchar(65533) not null,--可用
c2 varchar(65532),--可用
一个页的大小一般是16KB,也就是16384字节,而一个VARCHAR(M)类型的列就最多可以存储65533个字节,这
样就可能出现一个页存放不了一条记录。
在Compact和Reduntant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分
数据,把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址(当
然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数),从而可以找到剩余数据所在的页。
Redundant行格式
Redundant是MySQL 5.0版本之前InnoDB的行记录存储方式,MySQL 5.0支持Redundant是为了向前兼容性。
字段长度偏移列表:与 Compact 中的变长字段长度列表相同的是它们都是按照列的逆序顺序设置值的,不同的是字段长度偏移列表记录的是偏移量,每一次都需要加上上一次的偏移,同时对于 CHAR 的 NULL 值,会直接按照最大空间记录,而对于 VCHAR 的 NULL 值不占用任何存储空间。
Dynamic和Compressed行格式
这两种行格式类似于COMPACT行格式,只不过在处理行溢出数据时有点儿分歧,它们不会在记录的真实数据处
存储一部分数据,而是把所有的数据都存储到其他页面中,只在记录的真实数据处(相对来说为第一页,这一页存储了其他列数据,对于溢出的那一列只存储指针指向后面的页)存储其他页面的地址。另外,Compressed行格式会采用压缩算法对页面进行压缩。
行溢出数据的存储方式:
方式一:这种存储方式一条数据都占据了一页的大量的空间
方式二:相对来说的第一页只存指针,指向不同的页的记录