数据库中表的数据被划分为若各个页(page),每个页中又存储了很多行记录,而我们往MySQL中插入的每行记录就放到页当中的行记录中,InnoDB的页分为以下几个部分
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部分替代掉之后,也就意味着这个页使⽤完了,如果还有新的 记录插⼊的话,就需要去申请新的页了。
我们往User Records部分插入记录的时候,是直接在现有行的末尾(在顶部可用空间部分)或者删除行留下空间的任何地方插入,而并不是按照主键顺序插入新行(设计到大量数据的移动)。
删除一条数据的时候mysql并不会马上将其中的数据回收,而是将这条记录置为已删除,下次插入新数据的时候就会重用这部分空间 但是为了更好的管理页中的记录,MySQL保证了页中的记录在逻辑上是有序的,这是如何做到的呢?
行记录格式中有一个next_record字段,它表⽰从当前记录的真实数据到下⼀条记录的真实数据的地址偏移量。⽐⽅说第⼀条记录的next_record值为32,意味着从第⼀条记录的真实数据的地址处向后找32个字节便是 下⼀条记录的真实数据。
关于行格式可以参见我的上一篇文章
如果你熟悉数据结构的话,就⽴即明⽩了,这其实是个链表,可以通过⼀条记录找到它的下⼀条记录。但是需要注意注意再注意的⼀点是,下⼀条记录指得并不是按照我们插⼊顺序的下⼀条记录,⽽是按照主键值由⼩到⼤的顺序的下⼀条记录。
⽽且规定 Infimum记录(也就是最⼩记录) 的下⼀条记录就是本页中主键值最⼩的⽤户记录,⽽本页中主键值最⼤的⽤户记 录的下⼀条记录就是 Supremum记录(也就是最⼤记录) ,为了更形象的表⽰⼀下这个next_record起到的作⽤,我们⽤箭头来替代⼀下next_record中的地址偏移量
同时无论增删改任何一个操作,mysql都会保证这个链表的数据是自增的。
页目录
当我们在一个页中查询数据的时候只用从最小记录开始往后遍历就能找到我们需要的记录了,但是如果一个页中记录数少还好,如果数据比较多,这样查找下来就比较耗费性能了,mysql当然不会这么干。
我们平常想从⼀本书中查找某个内容的时候,⼀般会先看⽬录,找到需要查找的内容对应的书的页码,然后到对应的页码查看内容。InnoDB为我们的记录也制作了⼀个类似的⽬录,他们的制作过程是这样的
-
将所有正常的记录(包括最⼤和最⼩记录,不包括标记为已删除的记录)划分为⼏个组
-
每个组的最后⼀条记录(也就是组内最⼤的那条记录)的头信息中的n_owned属性表⽰该记录拥有多少条记录,也就是该组内共有⼏条记录。
-
将每个组的最后⼀条记录的地址偏移量单独提取出来按顺序存储到靠近⻚的尾部的地⽅
这个地⽅就是所谓的Page Directory