《学习InnoDB:核心之旅》中,我介绍了innodb_diagrams项目来记录InnoDB的内部,它提供了这篇文章中用到的图表。(请注意,下面的每张图片都链接到同一张图片的更高分辨率版本。)
空间和每个页面的基本结构已经在InnoDB空间文件布局基础中描述过,现在我们将升入研究索引页面的物理结构,这将为逻辑或者更高级别之上对索引的讨论打下基础。
在InnoDB中一切都是索引
在深入讨论物理结构之前,有一点很重要,那就是在InnoDB中,所有的内容都是索引,这对物理结构意味着什么?
- 1.每张表有一个主键,如果创建表没有指定,则将使用第一个非空的唯一键,如果没有指定,则会自动将一个48位隐藏的RowID字段添加到表结构中,并用作主键。总是自己添加一个主键,隐藏的对你来说是无用的,但是每行仍然花费6个字节。
- 2.行数据,非主键字段存储在主键的索引结构中,也成为聚集索引,这个索引的结构以主键字段为键,行数据是附加到该键的值。以及MVCC的一些额外的字段。
- 3.第二索引存储在相同的索引结构中,但是他们按键字段设置键,而主键值PKV附加到哪个键上。
当讨论InnoDB中的索引时,这实际上指的是DBA认为的表和索引。
索引的结构和概述
每个索引的整体结构都如下:
页面结构的主要部分为(不按顺序):
- The FIL header and trailer:这是一个典型的类型,在所有的页面类型中。索引页的一个独特之处在于,FIL标题中的上一页和下一页指针指向同一级别的索引中的上一页和下一页。并根据索引的键按顺序排列,这将形成每个级别上所有页面的双向链表,这将在逻辑索引结构中进一步描述。
- FSEG header:正如在InnoDB空间文件和页面管理中所描述的那样,索引的根页面的FSEG头文件包含指向该索引所使用的文件段的指针。所有的其他索引页的FSEG标题是未使用的和零的填充。
- The INDEX header:包含许多事与索引页和记录管理相关的字段,下面描述。
- System records: InnoDB在每个页面上都有两条系统记录,分别被叫做infimum和supermum,这些记录存储在页中的一个固定位置,以便总是可以直接根据页中的字节的offset找到他们。
- User records:实际的数据,每个记录都有一个可变宽度的标题和实际的列数据本身,标题包含下一个记录的指针,它按升序存储页面中下一个记录的offset,形成一个单独的链表的列表,用户记录结构的细节将在后面的帖子中描述。
- The page directory:页面目录从FIL开始,从页面的顶部向下增长,包含指向页面中某些记录的指针,每4-8条记录一次,关于页面的美丽的逻辑结构及其用途的细节将在以后的文章中详细描述。
索引标题
每个索引的标题都是具有固定的宽度,其结构如下:
这个结构存储的的字段如下:
- Index ID: 此页面的索引ID。
- Format Flag:该页中记录的格式,存储在堆记录数字段的高位0x8000中,两个值可能是,紧凑COMPACT 和冗余REDUNDANT,下面将全部进行描述。
- Maximum Transaction ID:对该页中任何记录的任何修改的最大的事务ID。
- Number of Heap Records: 页中记录的总数,包括Infimum和superemum,以及已删除的垃圾记录。
- Number of Records:页面中未删除的用户记录的数量。
- Heap Top Position: 当前使用空间的结束字段的offset,堆顶部和页目录末尾之间的所有空间都是空闲的。
- First Garbage Record Offset:一个指向垃圾记录列表中的第一个条目的指针,该列表使用的每个记录头重的下一个记录指针单独链接在一起,这在InnoDB中被认为free,但是这个名字优点让人迷惑。
- Garbage Space: 垃圾记录列表中被删除的记录所消耗的总字节数。
- Last Insert Position: 最后插入到页中的记录的字节的offset。
- Page Direction:目前有三个值用于页面的方向,LSFT,RIGHT和NO_DIRECTION,这表示该页正在经历顺序插入,向左或者向右随机插入,在每次插入时,读取最后一个插入位置的记录,并将KEY与当前插入的记录的KEY进行比较,以确定插入的方向。
- Number of Inserts in Page Direction: 一旦设置了页面的方向,接下来任何插入都不会重叠方向,由于他们的方向不同,会导致这个值增加。
- Number of Directory Slots: 页牡蛎在槽中的大小,每个槽位是16字节的offset。
- Page Level:索引中该页的级别,叶子页面处于0级,并且从这里开始在B+树中递增,在一个典型的三级B+树种,根是级别2,一些内部非叶子页是级别1,叶子页级别是0,这将在后面的文章中详细讨论,因为他与逻辑结构有关。
记录格式:冗余与紧凑
紧凑的记录格式COMPACT是Barracuda表格式中的新格式,而冗余的记录格式REDUNDANT是最初的Antelope表格式,在创建Barracuda之前,这两种格式都没有正式的名称。紧凑的格式主要消除了在每个记录中冗余存储的信息,这些信息可以从数据字典中获得,比如字段的数量,哪些字段可以为空,以及哪些字段是动态长度。
一个旁注记录指针
记录指针在几个不同的地方使用,索引头中的最后一个插入位置的字段,页面目录中的所有值,以及系统记录和用户记录中的下一个记录指针,所有记录都包含一个头,可能是可变宽度的,后面跟着的实际记录数据也可能是可变宽度的。记录指针指向记录数据第一个字节的位置,他有效的介于头和记录数据之间,这允许通过从该位置向后读取头文件,并从该位置向前读取记录数据。
由于系统和用户记录中的下一个记录指针总是从头中的第一个字段从该指针向后读取,这意味着也可以非常有效地地区页面中的所有记录而不需要解析变宽的记录数据。
系统记录:infilum和supremum
每个所有页包含两个系统记录,称为infimum和supremun,在固定位置,offset分别为99和112的页内,结构如下:
这两条系统记录在他们之前的位置有一个典型的记录头,文字字符串infimum和supremum,做为他们唯一的数据,记录头字段的完整描述将在以后的文章中提供,现在,最重要的是观察第一个字段如前所述,从记录数据向后工作是下一个记录的指针。
infimum 记录
infimum记录表示低于页面中任何可能的键的值,它的下一条记录指针指向页面中键值最低的用户记录,infimum充当按顺序扫描用户记录的固定入口。
supremum激励
suptemum表示一个高于页面中任何可能键值的键,它的下一个记录的指针总是0,表示NULL并且由于页面标题的原因,他总是一个实际记录的无效位置。页面上键最高的用户记录的下一个记录指针总是指向supremum.
用户记录
用户记录的实际磁盘上的格式将在后面说明,因为它相当复杂,本身需要很长的说明。
用户记录按照拆入的顺序别添加到页面的主体中,并且可能从先前删除的记录中获取现有的为空的空间。并且通过使用每个记录头重的下一个记录指针,按升序进行单独的链接。一个单链表由infimum组成,以升序的方式通过所有的用户记录,并以spremum终止,使用这个列表,以升序扫描一个页面中的所有用户记录是很简单的。
此外,使用在开头说的下一页的指针,很容易在整个索引中按2升序逐页扫描,这意味着一个上升顺序的表扫描也很容易实现。
- 1.从索引的第一个页开始,这个页面是通过B+树遍历找到的,这将在后面详细介绍。
- 2.读取infimum,并尊选它的下一个记录的指针。
- 3.如果该记录是supremum,青继续执行步骤5.如果没有,读取并处理记录内容。
- 4.跟随下一个记录的指针返回步骤3.
- 5.如果下一页指针指向NULL,退出,如果没有,跟所下一页指针返回步骤2.
由于记录是单链接的,而不是双链接的,因此按降序遍历并不简单,将在后续的文章中讨论。
页目录
页面目录此从FIL页脚开始,从哪里向下增长到用户记录,页面目录包含一个指向每4-8条记录的指针,此外总是包含一个关于起始最高的条目。
页面目录只是一个动态大小的16位offset的指针数组,这些指针指向页面内的记录。它的目的将在要给专门的网页目录的帖子中更加充分的描述。
空闲空间
用户记录向上增长和页面记录向下增长,之间的空间被认为始空闲空间,一旦这两个部分在中间相遇,耗尽了空闲空间,则认为该页已满,假设通过重写组织以删除垃圾无法回收的空间。
下一章概览
接下来我们将研究索引的逻辑结构包括一些示例。