InnoDB数据页的结构(FIL_PAGE_INDEX)
InnoDB引擎内部存放着各种各样的页(page),这些page也分为各种不同的类型。如:FIL_PAGE_INDEX数据页(带有真实数据的)、FIL_PAGE_TYPE_ALLOCATED未分配页、FIL_PAGE_TYPE_BLOG溢出页……等十多个类型的页,用于不同的用途:
补充:页面管理的分类:
- 空闲页:空闲页
- 数据页:干净页
- 脏页:跟磁盘数据不一致,需要生效
- File Header:文件头部,38字节
- Page Header:页面头部,56字节
- Infimum+Superman:页面中的最小记录+最大记录。是虚拟记录
- User Records:用户记录,真实数据
- Free Page:空闲空间,尚未使用的空间
- Page Directory:页目录
- File Trailer:文件尾
- 所有未删除的记录将被划分为若干个分组,包括伪记录
- 每个组的最后一条记录的n_owned属性标记着当前分组的记录数量
- 每个组的最后一条记录在页面中的地址偏移量提取出来,按顺序放置到靠近页尾的地方,这个地方就称之为页目录(Page Directory),这些偏移量也称为“槽”,每个槽占2字节,页目录就是由多个槽组成的。
- Infimum所在的分组,只能有Infimum记录本身,即1条记录
- Superman所在的分组记录条数只能在1~8条之间,剩下其他分组条数范围在4~8条之间
- 分组实现步骤:
- 初始状态,一个page只有两条记录,两个分组,两个槽,分别记录Infimum和Superman的地址偏移量
- 每插入一条记录,都会从页目录中找到对应记录的主键值比待插入记录的主键值大且差值最小的槽(没懂),然后把该槽对应的记录的n_owned值+1,表示本组又添加了一条记录,直至本组记录数等于8
- 当一个组的记录数等于8后,再插入一条记录时,会讲组中的记录拆分成两个组,其中一个组4条记录,一个组5条记录。拆分过程中会在页目录中新增一个槽,记录这个新增分组中最大的那个记录的偏移量
- 初始状态下,设置low为0,hight为槽的总数量, 槽的数量也就是分组的数量。由于槽本身记录着其分组最后一条记录的偏移量,所以槽所对应的主键就是槽所在分组的最大主键。使用需要查找的主键跟槽对比,修改low和hight两个变量,最后对比出需要查找的记录具体在哪个槽。
- 知道某个槽(分组)中存放着我想要的记录,则需要把这个槽从小到大逐个遍历,我们拿到的槽的值是不可以用来遍历的,因为槽的值只是当前分组的最大记录,不能反向去遍历。而页目录中的槽都是连续存放的,因此我们需要拿到上一个槽,获取那条记录的下一条记录,也就是next_records值,然后遍历所在分组的所有记录,即可找到该主键对应的记录。
- 预留1、2位:预留位,不用
- delete_flag:标记是否删除。mysql中的做记录删除并没有删除记录所在占的空间,而是讲该记录置为1.如果删除时连带空间也删除,则每次删除一条记录都需要重新排列其他记录,造成性能消耗。因此使用该标记。并且所有被删除的记录就组成一个垃圾链表。记录在该链表中的空间时可重用空间,后续新增数据时,又可能会覆盖掉记录,并占用其空间。
- min_rec_flag:B+树中的每层非叶子节点中的最小目录项记录会添加该标记
- n_owned:页面内的记录分成的组,每个组中有一个记录的你n_owned值记录着当前记录组的条数,剩余记录该值均为0
- Heap_no:当前记录在页面堆中的相对位置。
- Record_type:记录类型,普通记录标记0;非叶节点的目录标记1,Infimum标记2,Superman标记3
- Next_records:表示到下一条距离的相对位置。该属性记录当前记录的真实数据到下一条记录真实数据的距离。如果某条记录该属性位正数,则下一条记录在该记录的后面。例如:第一条记录该属性值为32,则下一条的真实数据地址在本条记录的真实数据的地址往后+32字节;如果某条记录该属性为负数-145,则下一条记录的真实数据需要往前-145字节。