第 9章 存放页面的大池子——InnoDB的表空间

知识回顾 #

表空间是一个抽象的概念,对于系统表空间来说,对应着文件系统中一个或多个实际文件;对于每个独立表空间来说,对应着文件系统中一个名为“表名.ibd”的实际文件。

页面类型 #

InnoDB是以页为单位管理存储空间的。我们的聚簇索引和其他的耳机索引都是以B+树的形式保存到表空间中的,而B+树的节点就是数据页(这种数据页的类型是FIL_PAGE_IDNEX)。

InnoDB为不同的目的设计了多种不同类型的页面

页面通用部分 #

数据页(即INDEX类型的页)由7部分组成,由两个部分是所有类型的页面通用的:

  • File Header:记录页面的一些通用信息。
  • File Trailer:校验页是否完整,保证页面在从内存刷新到磁盘后内容是相同的。

File Header部分组成部分如下表所示:

名称大小描述
FILE_PAGE_OFFSET4字节页号
FILE_PAGE_PREV4字节上一个页的页号
FILE_PAGE_NEXT4字节下一个页的页号
FILE_PAGE_LSN8字节页面被最后修改时对应的LSN(日志序列号),用于恢复
FILE_PAGE_TYPE2字节页面类型
FILE_PAGE_FILE_FLUSH_LSN8字节仅在系统表空间的第一个页中定义,代表文件至少被刷新到了对应的LSN值
FILE_PAGE_ARCH_LOG_NO_OR_SPACE_ID4字节页属于哪个表空间

着重强调的点:

  • 表空间中的每一个页都对应着一个页号,也就是FILE_PAGE_OFFSET,我们可以通过这个页号在表空间中快速定位到指定的页面。
  • 某些类型的页面可以组成链表,链表中相邻的两个页面的页号可以不连续,也就是说它们可以不按照在表空间中的物理位置相邻存储,而是根据FILE_PAGE_PREV和FILE_PAGE_NEXT来存储上一个页和下一个页的页号。
  • 每个页的类型由FILE_PAGE_TYPE表示。

独立表空间结构 #

由于表空间中的页太多,为了更好的管理页,InnoDB提出了区(extent)的概念。 一个区默认占用1MB空间大小。无论是系统表空间还是独立表空间,都可以看成是由若干个连续的区组成的,每256个区被划分成一组。

#

段是一些零散的页面以及一些完整的区的集合

为什么要有区 #

如果以页为单位来分配存储空间,双向链表相邻的两个页之间的物理位置可能离得非常远。对于传统的机械硬盘来说,需要重新定位磁头位置,也就是会产生随机I/O,会响应磁盘性能。所以应该尽量让页面链表中相连的页的物理位置页相邻,这样在扫描叶子节点中大量的记录时才可以使用顺序I/O。 所以,才引入了区的概念。

总结: 虽然B+树每层节点(页)在物理上不必相邻,但是InnoDB在设计上会使其尽量相邻。目的:减少随机I/O。

为什么要有段 #

为了区分叶子节点和非叶子节点。

叶子节点有自己独有的区,非叶子节点也有自己独有的区。存放叶子节点的区的集合就是一个段,存放非叶子节点的区的集合也是一个段。 一个索引会生成两个段:一个叶子节点段和一个非叶子节点段。

小表与段 #

使用InnoDB存储引擎的表只有一个聚簇索引,一个索引会生成两个段,而段是以区为单位申请存储空间的,一个区默认占用1MB存储空间。 对于只存储几条记录的小表来说,占用2MB存储空间显得浪费。

碎片区 #

为了让小表能充分利用存储空间,InnoDB设计了 碎片区:

  • 碎片区中的页可以用于不同的目的,可以用于不同的段。
  • 碎片区直属于表空间,并不属于任何一个段。

为段分配存储空间的策略是: #

  • 刚开始向表中插入数据时,段时从某个碎片区以单个页面为单位来分配存储空间的。
  • 当某个段已经占用32个碎片区页面之后,就会以完整的区为单位来分配存储空间(原先占用的碎片区页面并不会被复制到新申请的完整的区中)。

区的分类 #

区大致可以分为4种类型:

  • 空间的区:区中没有任何页面被使用。
  • 有剩余空闲页面的碎片区:表示碎片区中还有可以被分配的空闲页面。
  • 没有剩余空闲页面的碎片区:表示碎片区中所有的页面都被分配使用,没有空闲页面。
  • 附属于某个段的区。
    书中对这部分概念很多,但是我觉得对于后端开发者来说不是很重要,所以就不做记录了。

真实表空间对应的文件大小 #

.ibd文件是自扩展的,随着表中数据的增多,表空间对应的文件页主键增大。

系统表空间 #

系统表空间的结构与独立表空间基本类似,只不过由于整个MySQL进程只有一个系统表空间,系统表空间开头有许多记录整个系统属性的页面

InnoDB数据字典 #

MySQL除了保存着插入的用户数据之外,还需要保存许多额外的数据。这些数据页成为元数据。InnoDB存储引擎特意定义el一系列内部系统表来记录这些元数据。 这些额外数据包括:

  • 某个表属于哪个表空间,表里面有多少列;
  • 表对应的每一个列的类型是什么;
  • 该表有多少个索引,每个索引对应哪几个字段,该索引对应的根页面在哪个表空间的哪个页面。
  • 该表有哪些外键,外键对应哪个表上的哪些列。
  • 某个表空间对应的文件系统上的文件路径是什么。
MySQL数据字典用来存存储关于数据库、表、索引、列等对象的元数据。