在innodb中页是存储空间的基本单位,一个页的大小一般是16kb。innodb为了不同的目的而设计了多种不同类型的页,比如存放表空间头部信息的页,存放change buffer信息的页,存放inode信息的页,存储undo日志信息的页等等,这里这要说说存放表中记录的那种类型的页,官方称这种存放记录的页为索引(index)页,有时候也会称之为数据页。
上图就是innodb数据页结构的示意图
在上面7个部分中,我们自己的数据(记录)会按照指定的行格式存储到User Records部分。但是在一开始生成页的时候,其实并没有User Records部分,每当插入一条记录时,都会从Free Space部分(也就是尚未使用的存储空间)申请一个记录大小的空间,并将这个空间划分到User Records部分。当Free Space部分的空间全部被User records部分替代掉之后,也就意味着这个页使用完了,此时如果还有新的记录插入,就需要申请新的页了。
由于页中存储的都是记录,因为在了解页之前,我们先看看记录,而关于记录在上一节的行格式中已经提到了,一条记录主要是由记录额外信息和记录真实信息两部分组成,其中记录真实信息就是我们所看到的各个列下面的数据(这个就很简单了),那么主要说说记录额外信息了,关于记录额外额外信息主要有1)变长字段字段长度列表 2)NULL值 3)记录头信息三部分组成,其中关于1和2这里就不说了,主要就看3中所谓的记录头信息。
看如下结构如下:
各个属性解释:
名称 | 大小 | 描述 |
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
delete_flag | 1 | 标记该记录是否被删除 |
min_rec_flag | 1 | B+树中每层非叶子节点中的最小的目录项记录都会添加该标记 |
n_owned | 4 | 一个页面中的记录会被分成若干组,每个组中的记录有一个" 带头大哥",其余记录都是“小弟”。“带头大哥”记录的n_owned 值代表该组中所有的记录条数,"小弟"记录的n_owned都是0 |
heap_no | 13 | 表示当前记录在页面堆中的相对位置 |
record_type | 3 | 表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点 的目录项记录,2表示infimum记录,3表示Supremum记录 |
next_record | 16 | 表示下一条记录的相对位置 |
这里模拟一下几条记录,介绍一条记录按照如下格式进行记录的话:
这里有4条记录,按照上面的记录格式进行填写的
这里使用箭头替代next_record的值,从上图可以看出,记录按照主键从小到大的顺序形成一个单向链表。Supremum记录的next_record值为0,也就是说Supremum记录之后就没有下一条记录了,这也意味着Supremum记录就是这个单向链表中的最后一个节点,如果从表中删除一条记录,这个由记录组成的单向链表也是会跟着变化。
所以无论怎么对页中的记录进行增删改查操作,innodb始终会维护记录的一个单向链表,链表中的各个节点是按照主键值由小到大的顺序链接起来的。
这里再看一个有意思的地方:主键值为2的记录被删除掉了,但是却没有回收存储空间(该记录的heap_no也未发生改变),如果我们在把这条记录插入到表中,会发生什么呢?
这里可以看到,innodb并没有因为新的记录的插入而为它申请新的存储空间,而是直接复用了原理被删除记录的存储空间。