InnoDB的数据页结构

概述

InnoDB 为了不同的目的而设计了许多种不同类型的 页 ,比如存放表空间头部信息的页,存放 Insert Buffer信息的页,存放 INODE 信息的页,存放 undo 日志信息的页等。而用来存放用户保存数据的页就是 innoDB的基本储存结构:索引页。(即数据页)。

它是 InnoDB 管理存储空间的基本单位,刷盘(从内存到硬盘)、读取操作,都是每次一页。

页面最大为16KB

innoDB的索引数据结构为B+树,而树的每个节点则为“页”,B+树同一层级的页为双向链表连接。

mysql innodb 索引 mysql innodb索引存储位置_mysql

一张数据页会包含以上的部分,其中,(最小记录、最大记录、用户记录、空闲空间),为行记录(自造的词语,即储存时遵守行格式的结构)。

页结构介绍

文件头部

File Header 针对各种类型的页都通用,也就是说不同类型的页都会以 File Header 作为第一个组成部分,它描述了一些针对各种页都通用的一些信息,比方说这个页的编号是多少,它的上一个页、下一个页是谁。这个部分占用固定的 38 个字节

mysql innodb 索引 mysql innodb索引存储位置_mysql_02

FIL_PAGE_SPACE_OR_CHKSUM
这个代表当前页面的校验和(checksum)。

对于很长的字节串来说,会通过某种算法(比如MD5)来计算/压缩为一个较短的值来表示,即为校验和。

比较两个数据是否相同,则可以先比较其校验和,如果校验和都不同,则数据肯定不同,能够大大降低比较的空间和时间。

FIL_PAGE_TYPE
这个代表当前 的类型, InnoDB 为了不同的目的而把页分为不同的类型,我们存放记录的数据页的类型其实是 FIL_PAGE_INDEX ,也就是所谓的 索引页 。

文件尾部

同样有校验和字段,用于和文件头的校验和相对比。

以保证刷盘的时候数据保存完整。防止刷盘刷到一半,断电、宕机。这样

页目录

innoDB会对数据进行分组,将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到靠近页的尾部的地方,这个地方就是所谓的 Page Directory ,也就是 页目录 。页面目录中的这些地址偏移量被称为 槽 (英文名: Slot ),所以这个页面目录就是由 槽 组成的。

mysql innodb 索引 mysql innodb索引存储位置_数据库_03

mysql innodb 索引 mysql innodb索引存储位置_java_04

我们在查找数据的时候,在定位到数据在某一页的情况下,该如何在页内的数据中进行快速查找呢?

我们知道,页内的数据组成的是一个链表,如果查找只能挨个遍历,这样就很慢,此时就有了页目录这个东西。

  1. innoDB会对用户记录进行分组,其中,最小记录单独一组,最大记录跟尾部的记录一组。最大记录所在的分组数量为1-8条,普通记录所在分组4-8条。
  2. 在向其中添加数据时,会在页目录中找到比当前主键值大,且数值最接近的组,并对其对应记录的n_owned 值加1。
  3. 直到 一组的数量达到8,此时再向该组中插入数据会将组中的记录拆分成两组,一组4条一组5条,页目录中相应的也会增加一个槽。

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

  1. 通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录。
  2. 通过记录的 next_record 属性遍历该槽所在的组中的各个记录

页面头部

为了能得到一个数据页中存储的记录的状态信息,比如本页中已经存储了多少条记录,第一条记录的地址是什么,页目录中存储了多少个槽等等,特意在页中定义了一个叫 Page Header 的部分,它是页 结构的第二部分, 这个部分占用固定的 56 个字节,专门存储各种状态信息 。页面头部的组成部分如下:

mysql innodb 索引 mysql innodb索引存储位置_mysql innodb 索引_05

PAGE_DIRECTION

假如新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边。用来表示最后一条记录插入方向的状态就是 PAGE_DIRECTION 。

PAGE_N_DIRECTION

假设连续几次插入新记录的方向都是一致的, InnoDB 会把沿着同一个方向插入记录的条数记下来,这个条数就用 PAGE_N_DIRECTION 这个状态表示。当然,如果最后一条记录的插入方向改变了的话,这个状态的值会被清零重新统计。