Mysql原理篇之系统表空间---06

Posted 热爱编程的大忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql原理篇之系统表空间---06相关的知识,希望对你有一定的参考价值。

mysql原理篇之系统表空间---06


引言

上一篇文章,我们讲述了独立的表空间的整体设计思路和原理讲解,本篇文章我们将剩下没讲的系统表空间进行一波剖析。

了解完了独立表空间的基本结构,系统表空间的结构也就好理解多了,系统表空间的结构和独立表空间基本类似,只不过由于整个MySQL进程只有一个系统表空间,在系统表空间中会额外记录一些有关整个系统信息的页面,所以会比独立表空间多出一些记录这些信息的页面。因为这个系统表空间最牛逼,相当于是表空间之首,所以它的表空间 ID(Space ID)是0。


系统表空间的整体结构

系统表空间与独立表空间的一个非常明显的不同之处就是在表空间开头有许多记录整个系统属性的页面,如图:

可以看到,系统表空间和独立表空间的前三个页面(页号分别为012,类型分别是FSP_HDRIBUF_BITMAPINODE)的类型是一致的,只是页号为37的页面是系统表空间特有的,我们来看一下这些多出来的页面都是干啥使的:


除了这几个记录系统属性的页面之外,系统表空间的extent 1和extent 2这两个区,也就是页号从64~191这128个页面被称为Doublewrite buffer,也就是双写缓冲区。不过上述的大部分知识都涉及到了事务和多版本控制的问题,这些问题我们会放在后边的章节集中唠叨,现在讲述太影响用户体验,所以现在我们只唠叨一下有关InnoDB数据字典的知识,其余的概念在后边再看。


InnoDB数据字典

我们平时使用INSERT语句向表中插入的那些记录称之为用户数据,MySQL只是作为一个软件来为我们来保管这些数据,提供方便的增删改查接口而已。但是每当我们向一个表中插入一条记录的时候,MySQL先要校验一下插入语句对应的表存不存在,插入的列和表中的列是否符合,如果语法没有问题的话,还需要知道该表的聚簇索引和所有二级索引对应的根页面是哪个表空间的哪个页面,然后把记录插入对应索引的B+树中。所以说,MySQL除了保存着我们插入的用户数据之外,还需要保存许多额外的信息,比方说:

  • 某个表属于哪个表空间,表里边有多少列
  • 表对应的每一个列的类型是什么
  • 该表有多少索引,每个索引对应哪几个字段,该索引对应的根页面在哪个表空间的哪个页面
  • 该表有哪些外键,外键对应哪个表的哪些列
  • 某个表空间对应文件系统上文件路径是什么
  • balabala … 还有好多,不一一列举了

上述这些数据并不是我们使用INSERT语句插入的用户数据,实际上是为了更好的管理我们这些用户数据而不得已引入的一些额外数据,这些数据也称为元数据。InnoDB存储引擎特意定义了一些列的内部系统表(internal system table)来记录这些这些元数据


这些系统表也被称为数据字典,它们都是以B+树的形式保存在系统表空间的某些页面中,其中SYS_TABLESSYS_COLUMNSSYS_INDEXESSYS_FIELDS这四个表尤其重要,称之为基本系统表(basic system tables),我们先看看这4个表的结构:


SYS_TABLES表


这个SYS_TABLES表有两个索引:

  • NAME列为主键的聚簇索引
  • ID列建立的二级索引

SYS_COLUMNS表


这个SYS_COLUMNS表只有一个聚集索引:

  • (TABLE_ID, POS)列为主键的聚簇索引

SYS_INDEXES表


这个SYS_INDEXES表只有一个聚集索引:

  • (TABLE_ID, ID)列为主键的聚簇索引

SYS_FIELDS表


这个SYS_FIELDS表只有一个聚集索引:

  • (INDEX_ID, POS)列为主键的聚簇索引

Data Dictionary Header页面

只要有了上述4个基本系统表,也就意味着可以获取其他系统表以及用户定义的表的所有元数据。比方说我们想看看SYS_TABLESPACES这个系统表里存储了哪些表空间以及表空间对应的属性,那就可以:

  • SYS_TABLES表中根据表名定位到具体的记录,就可以获取到SYS_TABLESPACES表的TABLE_ID
  • 使用这个TABLE_IDSYS_COLUMNS表中就可以获取到属于该表的所有列的信息。
  • 使用这个TABLE_ID还可以到SYS_INDEXES表中获取所有的索引的信息,索引的信息中包括对应的INDEX_ID,还记录着该索引对应的B+数根页面是哪个表空间的哪个页面。
  • 使用INDEX_ID就可以到SYS_FIELDS表中获取所有索引列的信息。

也就是说这4个表是表中之表,那这4个表的元数据去哪里获取呢?没法搞了,只能把这4个表的元数据,就是它们有哪些列、哪些索引等信息硬编码到代码中,然后设计InnoDB的大叔又拿出一个固定的页面来记录这4个表的聚簇索引和二级索引对应的B+树位置,这个页面就是页号为7的页面,类型为SYS,记录了Data Dictionary Header,也就是数据字典的头部信息。除了这4个表的5个索引的根页面信息外,这个页号为7的页面还记录了整个InnoDB存储引擎的一些全局属性,说话太啰嗦,直接看这个页面的示意图:


可以看到这个页面由下边几个部分组成:

可以看到这个页面里竟然有Segment Header部分,意味着设计InnoDB的大叔把这些有关数据字典的信息当成一个段来分配存储空间,我们就姑且称之为数据字典段吧。由于目前我们需要记录的数据字典信息非常少(可以看到Data Dictionary Header部分仅占用了56字节),所以该段只有一个碎片页,也就是页号为7的这个页。

接下来我们需要细细唠叨一下Data Dictionary Header部分的各个字段:

  • Max Row ID:我们说过如果我们不显式的为表定义主键,而且表中也没有UNIQUE索引,那么InnoDB存储引擎会默认为我们生成一个名为row_id的列作为主键。因为它是主键,所以每条记录的row_id列的值不能重复。原则上只要一个表中的row_id列不重复就可以了,也就是说表a和表b拥有一样的row_id列也没啥关系,不过设计InnoDB的大叔只提供了这个Max Row ID字段,不论哪个拥有row_id列的表插入一条记录时,该记录的row_id列的值就是Max Row ID对应的值,然后再把Max Row ID对应的值加1,也就是说这个Max Row ID是全局共享的。
  • Max Table ID:InnoDB存储引擎中的所有的表都对应一个唯一的ID,每次新建一个表时,就会把本字段的值作为该表的ID,然后自增本字段的值。
  • Max Index ID:InnoDB存储引擎中的所有的索引都对应一个唯一的ID,每次新建一个索引时,就会把本字段的值作为该索引的ID,然后自增本字段的值。
  • Max Space ID:InnoDB存储引擎中的所有的表空间都对应一个唯一的ID,每次新建一个表空间时,就会把本字段的值作为该表空间的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表聚簇索引的根页面的页号。

记录表中之表的根页面页号相当于限制死了这几个表对应B+树的位置

  • Unused:这4个字节没用,跳过。

以上就是页号为7的页面的全部内容,初次看可能会懵逼(因为有点儿绕),大家多瞅几次。


information_schema系统数据库

需要注意一点的是,用户是不能直接访问InnoDB的这些内部系统表的,除非你直接去解析系统表空间对应文件系统上的文件。不过设计InnoDB的大叔考虑到查看这些表的内容可能有助于大家分析问题,所以在系统数据库information_schema中提供了一些以innodb_sys开头的表:

mysql> USE information_schema;
Database changed

mysql> SHOW TABLES LIKE 'innodb_sys%';
+--------------------------------------------+
| Tables_in_information_schema (innodb_sys%) |
+--------------------------------------------+
| INNODB_SYS_DATAFILES                       |
| INNODB_SYS_VIRTUAL                         |
| INNODB_SYS_INDEXES                         |
| INNODB_SYS_TABLES                          |
| INNODB_SYS_FIELDS                          |
| INNODB_SYS_TABLESPACES                     |
| INNODB_SYS_FOREIGN_COLS                    |
| INNODB_SYS_COLUMNS                         |
| INNODB_SYS_FOREIGN                         |
| INNODB_SYS_TABLESTATS                      |
+--------------------------------------------+
10 rows in set (0.00 sec)

information_schema数据库中的这些以INNODB_SYS开头的表并不是真正的内部系统表(内部系统表就是我们上边唠叨的以SYS开头的那些表),而是在存储引擎启动时读取这些以SYS开头的系统表,然后填充到这些以INNODB_SYS开头的表中。以INNODB_SYS开头的表和以SYS开头的表中的字段并不完全一样,但供大家参考已经足矣。这些表太多了,我就不唠叨了,大家自个儿动手试着查一查这些表中的数据吧哈~


以上是关于Mysql原理篇之系统表空间---06的主要内容,如果未能解决你的问题,请参考以下文章

Mysql原理篇之表空间---05

Mysql实战篇之可以通过删除记录来缩小表空间大小吗?--05

Mysql实战篇之count(*)这么慢,我该怎么办?--06

Mysql原理篇之undo日志--下--12

Mysql原理篇之Innodb如何调整磁盘与CPU之间的矛盾--07

MySQL高级篇之View视图讲解