基于 Mysql5.7

一、回顾 InnoDB 逻辑结构图

image.png
页结构对应的就是上图中的”page“。

二、页结构

”页“ 是InnoDB 管理存储空间的基本单位,一个”页“默认 16 KB。
在 InnoDB 中存在不同种类型的”页“,根据不同功能进行划分。
如:

  • 存放表空间头部信息的页
  • 存放 Insert Buffer 信息的页
  • 存放 INODE 信息的页
  • 存放 undo 日志信息的页

    2.1、InnoDB 存储引擎”页“结构

    image.png
    如上图,InnoDB 存储引擎”页“结构由七部分组成

  • File Header(文件头)

  • Page Header(页头)
  • Infimun + Supremum Records
  • User Records(用户记录,即行记录)
  • Free Space(空闲空间)
  • Page Directory(页目录)
  • File Tailer(文件结尾信息)

    File Header(文件头)

    File Header 用来记录页的一些头信息,如:页的编号,上一页,下一页等。由八部分组成,共占用 38 字节
名称 占用空间的大小 描述
FIL_PAGE_SPACE_OR_CHKSUM 4字节 页的校验和(checksum值)
FIL_PAGE_OFFSET 4字节 页号
FIL_PAGE_PREV 4字节 上一个页的页号
FIL_PAGE_NEXT 4字节 下一个页的页号
FIL_PAGE_LSN 8字节 页面被最后修改时对应的日志序列位置(Log Sequence Number)
FIL_PAGE_TYPE 2字节 该页的类型
FIL_PAGE_FILE_FLUSH_LSN 8 字节 仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的 LSN值
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4字节 页属于哪个表空间
  • FIL_PAGE_SPACE_OR_CHKSUM

    当前页面的校验和(checksum)。 通过算法把一个很长的字符串变成一个较短的唯一串,较短的唯一串叫做校验和。 比较两个很长的字符串直接对比校验和能够节省时间的损耗。

  • FIL_PAGE_OFFSET

    每一个也都有一个单独的页号。InnoDB 通过页号唯一确定一个页,并定位。

  • FIL_PAGE_TYPE | 类型名称 | 十六进制 | 描述 | | —- | —- | —- | | FIL_PAGE_TYPE_ALLOCATED | 0x0000 | 最新分配,还没使用 | | FIL_PAGE_UNDO_LOG | 0x0002 | Undo日志页 | | FIL_PAGE_INODE | 0x003 | 段信息节点 | | FIL_PAGE_IBUF_FREE_LIST | 0x004 | Insert Buffer 空闲列表 | | FIL_PAGE_IBUF_BITMAP | 0x005 | Insert Buffer 位图 | | FIL_PAGE_TYPE_SYS | 0x0006 | 系统页 | | FIL_PAGE_TYPE_TRX_SYS | 0x007 | 事务系统数据 | | FIL_PAGE_TYPE_FSP_HDR | 0x008 | 表空间头部信息 | | FIL_PAGE_TYPE_XDES | 0x009 | 扩展描述页 | | FIL_PAGE_TYPE_BLOB | 0x00A | BLOB页 | | FIL_PAGE_INDEX | 0x45BF | 索引页,也就是我们所说的数据页 |

  • FIL_PAGE_PREV和FIL_PAGE_NEXT

    InnoDB 以页单位存放数据,但是存放的数据可能占用较大的空间,InnoDB可能不可以一次性为这么多数据分配一个非常大存储空间, 于是需要把数据分散到多个不连续的页中存储,这时候就需要把这些页关联起来,通过 FIL_PAGE_PREV和FIL_PAGE_NEXT,分别指向上一页和下一页想成双向链表,把数据串起来,如下图: [MySQL]-[InnoDB]-逻辑结构-页结构 - 图3

Page Header(页头)

Page Header 记录了数据页的状态信息,如:本页存储了多少条记录,第一条记录地址,页目录中存储了多少槽等。由14部分组成,共占 56 字节。

名称 占用空间大小 描述
PAGE_N_DIR_SLOTS 2 字节 Page Directory(页目录)中的 Slot(槽)数。
PAGE_HEAP_TOP 2字节 还未使用的空间最小地址,也就是说从该地址之后就是 Free Space
PAGE_N_HEAP 2 字节 本页中的记录的数量(包括最大和最小记录以及标记为删除的记录)
PAGE_FREE 2字节 第一个已经标记为删除的记录地址(各个已删除的记录通过 next_record也会组成一个单链表,这个单链表中的记录可以被重新利用)
PAGE_GARBAGE 2字节 已删除记录占用的字节数
PAGE_LAST_INSERT 2字节 最后插入记录的位置
PAGE_DIRECTION 2字节 记录插入的方向
PAGE_N_DIRECTION 2字节 一个方向连续插入的记录数量
PAGE_N_RECS 2字节 该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录)
PAGE_MAX_TRX_ID 8字节 修改当前页的最大事务 ID,该值仅在二级索引中定义
PAGE_LEVEL 2字节 当前页在 B+树中所在的层级
PAGE_INDEX_ID 8 字节 索引 ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF 10字节 B+树叶子段的头部信息,仅在 B+ 树的 Root页定义
PAGE_BTR_SEG_TOP 10字节 B+树非叶子段的头部信息,仅在 B+树的 Root页定义

Infimum + Supremum

在 InnoDB 存储引擎中,每个数据页中都有两条虚拟的记录,用来限定记录的边界。
Infimum:该页中的最小记录,该页中主键最小。
Supremum:该页中的最大记录,该页中主键最大。
这两个值在页创建的时候创建,并且不会被删除。
在 Compact行格式和 Redundant 行格式下,两者占用的字节数不相同。
两条记录与数据记录的位置关系
image.png

User Records 和 FreeSpace

User Records :实际存储记录的内容,这些记录根据主键进行排序。
FreeSpace:空闲空间,链表数据结构。当一条记录被删除,该空间会被加入空闲链表中。

Page Directory(页目录)

MySQL 中页使用B+树进行存储,通过B+树搜索也只能搜索到页。
对于记录的查询仍然需要到页中查找。

页中存储了多条记录,直接通过遍历的方式效率低,索引如引入了 Page Directory 概念。
Page Directory 由一个个 slot(槽)组成,slot(槽)存储了真实记录的指针。
InnoDB 的 slot(槽)是一个稀疏目录(sparse directory),一个 slot(槽)可能属于多个记录,最少属于4条,最多属于8条。

每一个 slot(槽)按照顺序排列,而 slot(槽)对应的记录也是按照顺序排列。

通过 slot(槽)查找数据,就是二分法的查找方式。
首先通过二分确定了 slot(槽)的位置,然后遍历 slot(槽)的数据,最多遍历 8次,O(1)的复杂度。

而 Page(页)是在内存中的数据,整个操作是在内存中完成的,且查询数据都是排列整齐的数据,所以查询速度可以忽略不计。

File Trailer

InnoDB 存储引擎会把磁盘的数据以页(16KB)为单位加载到内存中,针对数据的修改直接在内存中完成。
然后在某个时间点将内存的数据写入到磁盘中。
而内存在写数据到磁盘的过程中可能出现断电等导致写操作中断。这时候页的数据就是不完整的。
File Trailer 就是为了检验页的完整性。
File Trailer 只有一个FIL_PAGE_END_LSN 部分,占用 8 字节。

  • 前四个字节:代表该页的 checksum值
  • 后四个字节:和 File Header 中的 FIL_PAGE_LSN 相同。

File Trailer 和 File Header 中的 FIL_PAGE_SPACE_OR_CHKSUM 和 FIL_PAGE_LSN 进行比较,以此来保证页的完整性。

三、知识点

1、页的类型

InnoDB 为了不同的目的设计了不同类型的页
image.png

2、槽(二分法)

InnoDB会为把页中的记录划分为若⼲个组,每个组的最后⼀个记录的地址偏移量作为⼀个槽,存放在Page Directory中,所以在⼀个页中根据主键查找记录是⾮常快的,分为两步:

  • 通过⼆分法确定该记录所在的槽。
  • 通过记录的next_record属性遍历该槽所在的组中的各个记录。

    3、File Header

    每个数据页的File Header部分都有上⼀个和下⼀个页的编号,所以所有的数据页会组成⼀个双链表。

    4、File Trailer

    为保证从内存中同步到磁盘的页的完整性,在页的⾸部和尾部都会存储页中数据的校验和和页⾯最后修改时对应的LSN值,如果⾸部和尾部的校验和和LSN值校验不成功的话,就说明同步过程出现了问题。