大家好。上篇文章我们讲了InnoDB的独立表空间,我们知道了表空间被划分为许多连续的区,对于大小为16KB的页面来说,每个区默认由64个页组成,每256个区为一组,每个组最开始的几个页的类型是固定的。(在这里强烈建议在看本篇文章之前先看一下上一篇文章)今天我们再来聊一聊–系统表空间。
系统表空间的结构和独立表空间基本类似,只不过由于整个MySQL进程只有一个系统表空间,在系统表空间中会额外记录一些有关整个系统信息的页面,所以会比独立表空间多出一些记录这些信息的页面。具体结构如下图所示:
我们可以看出系统表空间和独立表空间的前三个页面(页号分别为0、1、2,类型分别是FSP_HDR、IBUF_BITMAP、INODE)的类型是完全一样的,只是页号为3~7的页面是系统表空间特有的,这些多出来的页面功能如下所示:
页号 | 页面类型 | 英文描述 | 描述 |
---|---|---|---|
3 | SYS | Insert Buffer Header | 存储Insert Buffer的头部信息 |
4 | INDEX | Insert Buffer Root | 存储Insert Buffer的根页面 |
5 | TRX_SYS | Transction System | 事务系统的相关信息 |
6 | SYS | First Rollback Segment | 第一个回滚段的页面 |
7 | SYS | Data Dictionary Header | 数据字典头部信息 |
除了这几个记录系统属性的页面之外,系统表空间的extent1和extent 2这两个区被称为 Doublewrite buffer ,也就是双写缓冲区。
今天我们先聊一下InnoDB数据字典,其余的概念之后咱们聊。
InnoDB数据字典
每当我们向一个表中插入一条记录的时候,mysqld都要校验插入语句对应的表存不存在,插入的列和表中的列是否符合,如果语法没有问题的话,还需要知道该表的聚簇索引和所有二级索引对应的根页面是哪个表空间的哪个页面,然后把记录插入对应索引的B+树中。所以说,MySQL 除了保存着我们插入的用户数据之外,还需要保存许多额外的信息,比如:
某个表属于哪个表空间,表里边有多少列;
表对应的每一个列的类型是什么;
该表有多少索引,每个索引对应哪几个字段;
该索引对应的根页面在哪个表空间的哪个页面;
该表有哪些外键,外键对应哪个表的哪些列;
某个表空间对应文件系统上文件路径是什么。
这些数据并不是插入的用户数据而是为了更好的管理用户数据而不得 已引入的一些额外数据,这些数据也称为元数据。InnoDB存储引擎特意定义了一系列的内部系统表(internal system table)来记录这些这些元数据,内部系统表有以下几种:
表名 | 描述 |
---|---|
SYS_TABLES | 整个InnoDB存储引擎中所有的表的信息 |
SYS_COLUMNS | 整个InnoDB存储引擎中所有的列的信息 |
SYS_INDEXES | 整个InnoDB存储引擎中所有的索引的信息 |
SYS_FIELDS | 整个InnoDB存储引擎中所有的索引对应的列的信息 |
SYS_FOREIGN | 整个InnoDB存储引擎中所有的外键的信息 |
SYS_FOREIGN_COLS | 整个InnoDB存储引擎中所有的外键对应列的信息 |
SYS_TABLESPACES | 整个InnoDB存储引擎中所有的表空间信息 |
SYS_DATAFILES | 整个InnoDB存储引擎中所有的表空间对应文件系统的文件路径信息 |
SYS_VIRTUAL | 整个InnoDB存储引擎中所有的虚拟生成列的信息 |
这些系统表也就是所谓的数据字典,它们都是以B+树的形式保存在系统表空间的某些页面中,其中 SYS_TABLES 、SYS_COLUMNS 、 SYS_INDEXES、SYS_FIELDS 这四个表最为重要,称之为基本系统表(basic system tables),我们先看看这4个表的结构:
SYS_TABLES表
列名 | 描述 |
---|---|
NAME | 表的名称 |
ID | InnoDB存储引擎中每个表都有一个唯一的ID |
N_COLS | 该表拥有列的个数 |
TYPE | 表的类型,记录了一些文件格式、行格式、压缩等信息 |
MIX_ID | 已过时,忽略 |
MIX_LEN | 表的一些额外的属性 |
CLUSTER_ID | 未使用,忽略 |
SPACE | 该表所属表空间的ID |
这个SYS_TABLES表有两个索引:以NAME列为主键的聚簇索引和以ID列建立的二级索引。
SYS_COLUMNS表
列名 | 描述 |
---|---|
TABLE_ID | 该列所属表对应的ID |
POS | 该列在表中是第几列 |
NAME | 该列的名称 |
MTYPE | 主数据类型 |
PRTYPE | precise type,精确数据类型 |
LEN | 该列最多占用存储空间的字节数 |
PREC | 该列的精度,不过这列貌似都没有使用,默认值都是0 |
SYS_COLUMNS表只有一个聚集索引:以(TABLE_ID, POS)列为主键的聚簇索引。
SYS_INDEXES表
列名 | 描述 |
---|---|
TABLE_ID | 该索引所属表对应的ID |
ID | InnoDB存储引擎中每个索引都有一个唯一的ID |
NAME | 该索引的名称 |
N_FIELDS | 该索引包含列的个数 |
TYPE | 该索引的类型,比如聚簇索引、唯一索引、普通的二级索引等 |
SPACE | 该索引根页面所在的表空间ID |
PAGE_NO | 该索引根页面所在的页面号 |
MERGE_THRESHOLD | 如果页面中的记录被删除到某个比例,就把该页面和相邻页面合并,这个值就是这个比例 |
SYS_INEXES 表只有一个聚集索引:以(TABLE_ID, ID) 列为主键的聚簇索引。
SYS_INDEXES表
列名 | 描述 |
---|---|
INDEX_ID | 该索引列所属的索引的ID |
POS | 该索引列在某个索引中是第几列 |
COL_NAME | 该索引列的名称 |
这个SYS_INEXES 表只有一个聚集索引:以(INDEX_ID, POS) 列为主键的聚簇索引。
Data Dictionary Header 页面
只要有了上述4个基本系统表,我们就可以获取其他系统表以及用户定义的表的所有元数据。
比方说我们想看看SYS_TABLESPACES 这个系统表里存储了哪些表空间以及表空间对应的属性,我们可以执行下面操作:
到SYS_TABLES 表中根据表名定位到具体的记录,就可以获取到 SYS_TABLESPACES 表的TABLE_ID。
使用这个TABLE_ID到SYS_COLUMNS表中就可以获取到属于该表的所有列的信息。
使用这个TABLE_ID还可以到SYS_INDEXES表中获取所有的索引的信息,索引的信息中包括对应的INDEX_ID,还记录着该索引对应的B+树根页面是哪个表空间的哪个页面。
使用INDEX_ID 就可以到SYS_FIELDS表中获取所有索引列的信息。
那么这4个表的元数据去哪里获取呢?原来这4个表的元数据信息硬编码到了代码中,然后InnoDB用一个固定的页面来记录这4个表的聚簇索引和二级索引对应的B+树位置,这个页面就是页号为7的页面,类型为SYS,记录了Data Dictionary Header ,也就是数据字典的头部信息。除了这4个表的5个索引的根页面信息外,这个页号为7的页面还记录了整个InnoDB存储引擎的一些全局属性,如下图所示:
这个页面由下边几个部分组成:
名称 | 占用空间 | 简单描述 |
---|---|---|
File Header(文件头部) | 38 字节 | 页的一些通用信息 |
Data Dictionary Header(数据字典头部信息) | 52 字节 | 记录一些基本系统表的根页面位置以及InnoDB存储引擎的一些全局信息 |
Unused | 4 字节 | |
Segment Header(段头部信息) | 10 字节 | 记录本页面所在段对应的INODE Entry位置信息 |
Empty Space(尚未使用空间) | 16272字节 | 用于页结构的填充,没啥实际意义 |
File Trailer(文件尾部) | 8 字节 | 校验页是否完整 |
可以看到这个页面里有Segment Header 部分,意味着InnoDB把这些有关数据字典的信息当成一个段来分配存储空间。由于需要记录的数据字典信息非常少(可以看到Data Dictionary Header 部分仅占用了56字节),所以该段只有一个碎片页,也就是页号为7的这个页。
接下来我们聊一聊Data Dictionary Header部分的各个字段:
Max Row ID : 如果我们建表时没有定义主键,而且表中也没有 UNIQUE 索引,那么InnoDB存储引擎会默认生成一个名为row_id 的列作为主键。因为它是主键,所以每条记录的row_id列的值不能重复。在拥有 row_id列的表插入一条记录时,该记录的row_id列的值就是Max Row ID 对应的值,然后再把 Max Row ID 对应的值加1,也就是说这个Max Row ID 是全局共享的。
Max Table ID : InnoDB存储引擎中的所有的表都对应一个唯一的ID,每次新建一个表时,就会把本字段的值+1,然后作为该表的ID。
Max Index ID : InnoDB存储引擎中的所有的索引都对应一个唯一的ID,每次新建一个索引时,就会把本字段的值+1,作为该索引的ID。
Max Space ID : InnoDB存储引擎中的所有的表空间都对应一个唯一的ID,每次新建一个表空间时,就会把本字段的值+1,作为该表空间的ID。
Mix ID Low(Unused) : 这个字段没啥用,跳过。
Root of SYS_TABLES clust index : 表示SYS_TABLES表聚簇索引的根页面的页号。
Root of SYS_TABLE_IDS sec index : 表示SYS_TABLES表为ID列建立的二级索引的根页面的页号。
Root of SYS_COLUMNS clust index : 表示SYS_COLUMNS表聚簇索引的根页面的页号。
Root of SYS_INDEXES clust index: 表示SYS_INDEXES表聚簇索引的根页面的页号。
Root of SYS_FIELDS clust index : 表示SYS_FIELDS表聚簇索引的根页面的页号。
information_schema 系统数据库
用户是不能直接访问InnoDB 的这些内部系统表的,但是InnoDB在系统数据库 information_schema 中提供了一些以 innodb_sys 开头的表。
在information_schema 数据库中的这些以 INNODB_SYS 开头的表并不是真正的内部系统表(内部系统表就是上边说的以SYS 开头的那些表),而是在存储引擎启动时读取这些以SYS开头的系统表,然后填充到这些以 INNODB_SYS 开头的表中。
好了,今天的内容到这里就结束了,有什么疑问欢迎在评论区进行讨论。最后依旧是请各位老板有钱的捧个人场,没钱的也捧个人场,谢谢各位老板!