InnoDB数据页结构

前言

之前说过,记录是数据库操作的基本单位(如不理解请看InnoDB记录结构),数据存储在磁盘中,数据库操作在内存中进行,要完成对数据库的操作就要进行磁盘和内存的交互,但是如果数据众多,不可能让磁盘和内存进行频繁的交互,因为这是个低效率的行动,所以数据库推出了磁盘和内存交互的单位概念——页。
在明白记录的结构之后,也让我们了解一下页的结构。如果你学习过索引,你会了解索引的查找方式为什么会是那样的了。

页的结构清单

页的基本结构以及作用如下:
File Header:页的通用信息,占38字节
Page Header:包含页的一些专用信息,占56字节
infimun + supermun:两个伪记录,所占26字节
UserRcords:存储记录的空间,所占空间暂不确定
Free Space:空闲空间,所占空间不确定
Page Directory:页面目录,包含页中的某些记录的相对位置,所占空间不确定
File Tariler:文件尾部,主要用于校验数据,所占空间8字节

本篇文章不按照顺序讲解,按照复杂性从高到低讲解,有些模块单独讲解即可,但是内部空间的时候要涉及到多个模块。

记录在页中是怎么存储的

在页的7大结构中,我们存储的记录会按照行格式加入到UserRcords中。但是页刚刚创建时UserRcords其实并不存在,当记录插入时,会从Free Space中申请一块与记录大小相等的空间,这个空间就是UserRcords,每次插入都会执行这个步骤,直到Free Space的空间大小为 0 时表示页已满,不能再插入数据了。

这看似简单的操作,其实并不简单,在插入数据之后,记录的存储方式也需要研究,不能让页无序排列,但也不能按照顺序表的方式,因为这样的会出现空间碎片的问题,所以存储的方式就是单链表
学习过数据结构的都知道,对单链表的操作需要知道记录的相对位置,而这个信息存放在记录的记录头信息中。记录头信息在上篇文章中并没有具体说明,本篇文章将具体讲述。

记录头信息

delete_mask是删除标志位,占1bit位,当需要删除该记录时标志位置为1;没错,当删除一条记录时并没有将其从磁盘中移除,而是将所有标志位为1的记录创建成一个垃圾链表;因为直接删除会重新调整存储空间,不如等待下一条插入记录覆盖效率高。

n_owned表示当前记录拥有的记录数,页中的记录是以一组组的方式存储的,组头的n_owned表示本组共有多少条记录,而其中的记录的n_owned一般为 0。

heap_no表示当前记录在记录堆中的位置信息,通过比较主键来完成记录在记录堆中的排序,一般以 2 开头,以 n+1 结尾;为什么以2开头呢?因为 0 被 infimun 占据,1 被 supermun 占据。

next_record表示下一条记录的相对位置。举个例子,假如某个记录的next_record为32,则表示从当前记录往后找32字节就是下一条记录的真实数据存储位置,而supermun的next_record是 0,这意味着supermun记录在堆中的相对位置虽然是 1,但却是最后一条记录。具体记录存储如下图所示:

槽的概念

之前说过,页中记录的存储是以组的形式存在的,这里的组成为槽,一般最小记录单独作为一个槽,最大记录所在的槽可以存储 1~8 条记录,其他分组要求存储条数在 4~8 之间。所以分组是按照下边的步骤进行的:

  • 初始情况下一个数据也只有最小记录和最大记录两条记录,它们分属于两个分组
  • 之后每插入一条记录,都会从页目录中找到的主键值比本记录的主键值大且差值最小的槽,然后把该槽对应的记录的ne_owned的值加1,表示本组内有添加了一条记录,知道该组中的记录数等于8个
  • 在一个组中的记录数等于8个后再插入一条记录是,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个槽来记录这个新增分组中最大的那条记录的偏移量
    因为各个槽代表的记录的主键值都是从小到大排序的,所以我们可以使用所谓的二分法来进行快速查找。5个槽的编号分别是:0、1、2、3、4,所以初始情况下最低的槽就是low=0,最高的槽就是high=4。比方说我们想找主键值为6的记录,过程是这样的:

计算中间槽的位置:(0+4)/2=2,所以查看槽2对应记录的主键值为8,又因为8 > 6,所以设置high=2,low保持不变。

重新计算中间槽的位置:(0+2)/2=1,所以查看槽1对应的主键值为4,又因为4 < 6,所以设置low=1,high保持不变。

因为high - low的值为1,所以确定主键值为6的记录在槽2对应的组中。此刻我们需要找到槽2中主键值最小的那条记录,然后沿着单向链表遍历槽2中的记录。但是我们前边又说过,每个槽对应的记录都是该组中主键值最大的记录,这里槽2对应的记录是主键值为8的记录,怎么定位一个组中最小的记录呢?别忘了各个槽都是挨着的,我们可以很轻易的拿到槽1对应的记录(主键值为4),该条记录的下一条记录就是槽2中主键值最小的记录,该记录的主键值为5。所以我们可以从这条主键值为5的记录出发,遍历槽2中的各条记录,直到找到主键值为6的那条记录即可。由于一个组中包含的记录条数只能是1~8条,所以遍历一个组中的记录的代价是很小的。

所以在一个数据页中查找指定主键值的记录的过程分为两步:

通过二分法确定该记录所在的槽,并找到该槽所在分组中主键值最小的那条记录。

通过记录的next_record属性遍历该槽所在的组中的各个记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值