知识回顾 #
表空间是一个抽象的概念,对于系统表空间来说,对应着文件系统中一个或多个实际文件;对于每个独立表空间来说,对应着文件系统中一个名为“表名.ibd”的实际文件。
页面类型 #
InnoDB是以页为单位管理存储空间的。我们的聚簇索引和其他的耳机索引都是以B+树的形式保存到表空间中的,而B+树的节点就是数据页(这种数据页的类型是FIL_PAGE_IDNEX)。
InnoDB为不同的目的设计了多种不同类型的页面
页面通用部分 #
数据页(即INDEX类型的页)由7部分组成,由两个部分是所有类型的页面通用的:
- File Header:记录页面的一些通用信息。
- File Trailer:校验页是否完整,保证页面在从内存刷新到磁盘后内容是相同的。
File Header部分组成部分如下表所示:
| 名称 | 大小 | 描述 |
|---|---|---|
| FILE_PAGE_OFFSET | 4字节 | 页号 |
| FILE_PAGE_PREV | 4字节 | 上一个页的页号 |
| FILE_PAGE_NEXT | 4字节 | 下一个页的页号 |
| FILE_PAGE_LSN | 8字节 | 页面被最后修改时对应的LSN(日志序列号),用于恢复 |
| FILE_PAGE_TYPE | 2字节 | 页面类型 |
| FILE_PAGE_FILE_FLUSH_LSN | 8字节 | 仅在系统表空间的第一个页中定义,代表文件至少被刷新到了对应的LSN值 |
| FILE_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4字节 | 页属于哪个表空间 |
着重强调的点:
- 表空间中的每一个页都对应着一个页号,也就是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数据字典用来存存储关于数据库、表、索引、列等对象的元数据。