📓 Archive

09_INNODB_TABLE-SPACE

FGJ: Create:2024/06/07 Update: (2024-10-24)

prerequirement

通过前边儿的内容大家知道, 表空间 是一个抽象的概念,对于系统表空间来说,对应着文件系统中一个或多个实际文件;对于每个独立表空间来说,对应着文件系统中一个名为 表名.ibd 的实际文件。大家可以把表空间想象成被切分为许许多多个 页 的池子,当我们想为某个表插入一条记录的时候,就从池子中捞出一个对应的页来把数据写进去。本章内容会深入到表空间的各个细节中,带领大家在 InnoDB 存储结构的池子中畅游。由于本章中将会涉及比较多的概念,虽然这些概念都不难,但是却相互依赖,所以奉劝大家在看的时候:不要跳着看! 不要跳着看! 不要跳着看!

  • 回忆一些旧知识 #

    • 页面类型 #

      再一次强调,InnoDB是以页为单位管理存储空间的,我们的聚簇索引(也就是完整的表数据)和其他的二级索引都是以 B+ 树的形式保存到表空间的,而 B+ 树的节点就是数据页。我们前边说过,这个数据页的类型名其实是: FIL_PAGE_INDEX ,除了这种存放索引数据的页面类型之外,InnoDB也为了不同的目的设计了若干种不同类型的页面,为了唤醒大家的记忆,我们再一次把各种常用的页面类型提出来:

      因为页面类型前边都有个 FIL_PAGE 或者 FIL_PAGE_TYPE 的前缀,为简便起见我们后边唠叨页面类型的时候就把这些前缀省略掉了,比方说 FIL_PAGE_TYPE_ALLOCATED 类型称为 ALLOCATED 类型, FIL_PAGE_INDEX 类型称为INDEX 类型。

      类型名称十六进制描述
      FIL_PAGE_TYPE_ALLOCATED0x0000最新分配,还没使用
      FIL_PAGE_UNDO_LOG0x0002Undo日志页
      FIL_PAGE_INODE0x0003段信息节点
      FIL_PAGE_IBUF_FREE_LIST0x0004Insert Buffer空闲列表
      FIL_PAGE_IBUF_BITMAP0x0005Insert Buffer位图
      FIL_PAGE_TYPE_SYS0x0006系统页
      FIL_PAGE_TYPE_TRX_SYS0x0007事务系统数据
      FIL_PAGE_TYPE_FSP_HDR0x0008表空间头部信息
      FIL_PAGE_TYPE_XDES0x0009扩展描述页
      FIL_PAGE_TYPE_BLOB0x000ABLOB页
      FIL_PAGE_INDEX0x45BF索引页,也就是我们所说的 数据页
    • 页面通用部分 #

      我们前边说过数据页,也就是 INDEX 类型的页由7个部分组成,其中的两个部分是所有类型的页面都通用的。当然我不能寄希望于你把我说的话都记住,所以在这里重新强调一遍,任何类型的页面都有下边这种通用的结构:

      从上图中可以看出,任何类型的页都会包含这两个部分:
      1). File Header :记录页面的一些通用信息
      2). File Trailer :校验页是否完整,保证从内存到磁盘刷新时内容的一致性。

      对于 File Trailer 我们不再做过多强调,全部忘记了的话可以到将数据页的那一章回顾一下。我们这里再强调一遍 File Header 的各个组成部分:

      名称占用空间大小描述
      FIL_PAGE_SPACE_OR_CHKSUM4 字节页的校验和(checksum值)
      FIL_PAGE_OFFSET4 字节页号
      FIL_PAGE_PREV4 字节上一个页的页号
      FIL_PAGE_NEXT4 字节下一个页的页号
      FIL_PAGE_LSN8 字节页面被最后修改时对应的日志序列位置(英文名是:Log Sequence Number)
      FIL_PAGE_TYPE2 字节该页的类型
      FIL_PAGE_FILE_FLUSH_LSN8 字节仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值
      FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4 字节页属于哪个表空间

      现在除了名称里边儿带有 LSN 的两个字段大家可能看不懂以外,其他的字段肯定都是倍儿熟了,不过我们仍要强调这么几点:
      1). 表空间中的每一个页都对应着一个页号,也就是 FIL_PAGE_OFFSET ,这个页号由4个字节组成,也就是32个比特位,所以一个表空间最多可以拥有2³²个页,如果按照页的默认大小16KB来算,一个表空间最多支持64TB的数据。表空间的第一个页的页号为0,之后的页号分别是1,2,3…依此类推
      2). 某些类型的页可以组成链表,链表中的页可以不按照物理顺序存储,而是根据 FIL_PAGE_PREV 和FIL_PAGE_NEXT 来存储上一个页和下一个页的页号。需要注意的是,这两个字段主要是为了 INDEX 类型的页,也就是我们之前一直说的数据页建立 B+ 树后,为每层节点建立双向链表用的,一般类型的页是不使用这两个字段的。
      3). 每个页的类型由 FIL_PAGE_TYPE 表示,比如像数据页的该字段的值就是 0x45BF ,我们后边会介绍各种不同类型的页,不同类型的页在该字段上的值是不同的。

  • 独立表空间结构 #

    我们知道 InnoDB 支持许多种类型的表空间,本章重点关注独立表空间和系统表空间的结构。它们的结构比较相似,但是由于系统表空间中额外包含了一些关于整个系统的信息,所以我们先挑简单一点的独立表空间来唠叨,稍后再说系统表空间的结构。

    • 区(extent)的概念 #

      表空间中的页实在是太多了,为了更好的管理这些页面,设计 InnoDB 的大叔们提出了 区 (英文名: extent )的概念。对于16KB的页来说,连续的64个页就是一个 区 ,也就是说一个区默认占用1MB空间大小。不论是系统表空间还是独立表空间,都可以看成是由若干个区组成的,每256个区被划分成一组。画个图表示就是这样:


      其中 extent 0 ~ extent 255 这256个区算是第一个组, extent 256 ~ extent 511 这256个区算是第二个组, extent 512 ~ extent 767 这256个区算是第三个组(上图中并未画全第三个组全部的区,请自行脑补),依此类推可以划分更多的组。这些组的头几个页面的类型都是类似的,就像这样:


      从上图中我们能得到如下信息:
      一、). 第一个组最开始的3个页面的类型是固定的,也就是说 extent 0 这个区最开始的3个页面的类型是固定的,分别是:
      1.1). FSP_HDR 类型:这个类型的页面是用来登记整个表空间的一些整体属性以及本组所有的 区 ,也就是extent 0 ~ extent 255 这256个区的属性,稍后详细唠叨。需要注意的一点是,整个表空间只有一个 FSP_HDR 类型的页面。
      1.2). IBUF_BITMAP 类型:这个类型的页面是存储本组所有的区的所有页面关于 INSERT BUFFER 的信息。当然,你现在不用知道啥是个 INSERT BUFFER ,后边会详细说到你吐。
      1.3). INODE 类型:这个类型的页面存储了许多称为 INODE 的数据结构,还是那句话,现在你不需要知道啥是个 INODE ,后边儿会说到你吐。
      二、). 其余各组最开始的2个页面的类型是固定的,也就是说 extent 256 、 extent 512 这些区最开始的2个页面的类型是固定的,分别是:
      2.1). XDES 类型:全称是 extent descriptor ,用来登记本组256个区的属性,也就是说对于在 extent 256区中的该类型页面存储的就是 extent 256 ~ extent 511 这些区的属性,对于在 extent 512 区中的该类型页面存储的就是 extent 512 ~ extent 767 这些区的属性。上边介绍的 FSP_HDR 类型的页面其实和 XDES 类型的页面的作用类似,只不过 FSP_HDR 类型的页面还会额外存储一些表空间的属性。
      2.2). IBUF_BITMAP 类型:上边介绍过了。

      好了,宏观的结构介绍完了,里边儿的名词大家也不用记清楚,只要大致记得:表空间被划分为许多连续的区 ,每个区默认由64个页组成,每256个区划分为一组,每个组的最开始的几个页面类型是固定的就好了。

    • 段(segment)的概念 #

      为啥好端端的提出一个 区 ( extent )的概念呢?我们以前分析问题的套路都是这样的:表中的记录存储到页里边儿,然后页作为节点组成 B+ 树,这个 B+ 树就是索引,然后吧啦吧啦一堆聚簇索引和二级索引的区别。这套路也没啥不妥的呀~

      是的,如果我们表中数据量很少的话,比如说你的表中只有几十条、几百条数据的话,的确用不到 区 的概念,因为简单的几个页就能把对应的数据存储起来,但是你架不住表里的记录越来越多呀。

      ??啥??表里的记录多了又怎样? B+ 树的每一层中的页都会形成一个双向链表呀, File Header 中的FIL_PAGE_PREV 和 FIL_PAGE_NEXT 字段不就是为了形成双向链表设置的么?

      是的是的,您说的都对,从理论上说,不引入 区 的概念只使用 页 的概念对存储引擎的运行并没啥影响,但是我们来考虑一下下边这个场景:
      1). 我们每向表中插入一条记录,本质上就是向该表的聚簇索引以及所有二级索引代表的 B+ 树的节点中插入数据。而 B+ 树的每一层中的页都会形成一个双向链表,如果是以 页 为单位来分配存储空间的话,双向链表相邻的两个页之间的物理位置可能离得非常远。我们介绍 B+ 树索引的适用场景的时候特别提到范围查询只需要定位到最左边的记录和最右边的记录,然后沿着双向链表一直扫描就可以了,而如果链表中相邻的两个页物理位置离得非常远,就是所谓的 随机I/O 。再一次强调,磁盘的速度和内存的速度差了好几个数量级, 随机I/O 是非常慢的,所以我们应该尽量让链表中相邻的页的物理位置也相邻,这样进行范围查询的时候才可以使用所谓的 顺序I/O 。

      所以,所以,所以才引入了 区 ( extent )的概念,一个区就是在物理位置上连续的64个页。在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照 区 为单位分配,甚至在表中的数据十分非常特别多的时候,可以一次性分配多个连续的区。虽然可能造成一点点空间的浪费(数据不足填充满整个区),但是从性能角度看,可以消除很多的随机 I/O ,功大于过嘛!

      事情到这里就结束了么?太天真了,我们提到的范围查询,其实是对 B+ 树叶子节点中的记录进行顺序扫描,而如果不区分叶子节点和非叶子节点,统统把节点代表的页面放到申请到的区中的话,进行范围扫描的效果就大打折扣了。所以设计 InnoDB 的大叔们对 B+ 树的叶子节点和非叶子节点进行了区别对待,也就是说叶子节点有自己独有的 区 ,非叶子节点也有自己独有的 区 。存放叶子节点的区的集合就算是一个 段 ( segment ),存放非叶子节点的区的集合也算是一个 段 。也就是说一个索引会生成2个段,一个叶子节点段,一个非叶子节点段。

      默认情况下一个使用 InnoDB 存储引擎的表只有一个聚簇索引,一个索引会生成2个段,而段是以区为单位申请存储空间的,一个区默认占用1M存储空间,所以默认情况下一个只存了几条记录的小表也需要2M的存储空间么?以后每次添加一个索引都要多申请2M的存储空间么?这对于存储记录比较少的表简直是天大的浪费。设计InnoDB 的大叔们都挺节俭的,当然也考虑到了这种情况。这个问题的症结在于到现在为止我们介绍的区都是非常 纯粹 的,也就是一个区被整个分配给某一个段,或者说区中的所有页面都是为了存储同一个段的数据而存在的,即使段的数据填不满区中所有的页面,那余下的页面也不能挪作他用。现在为了考虑以完整的区为单位分配给某个段对于数据量较小的表太浪费存储空间的这种情况,设计 InnoDB 的大叔们提出了一个碎片(fragment)区的概念,也就是在一个碎片区中,并不是所有的页都是为了存储同一个段的数据而存在的,而是碎片区中的页可以用于不同的目的,比如有些页用于段A,有些页用于段B,有些页甚至哪个段都不属于。碎片区直属于表空间,并不属于任何一个段。所以此后为某个段分配存储空间的策略是这样的:
      1). 在刚开始向表中插入数据的时候,段是从某个碎片区以单个页面为单位来分配存储空间的。
      2). 当某个段已经占用了32个碎片区页面之后,就会以完整的区为单位来分配存储空间。

      所以现在段不能仅定义为是某些区的集合,更精确的应该是某些零散的页面以及一些完整的区的集合。除了索引的叶子节点段和非叶子节点段之外, InnoDB 中还有为存储一些特殊的数据而定义的段,比如回滚段,当然我们现在并不关心别的类型的段,现在只需要知道段是一些零散的页面以及一些完整的区的集合就好了。

    • 区的分类 #

      通过上边一通唠叨,大家知道了表空间的是由若干个区组成的,这些区大体上可以分为4种类型:
      1). 空闲的区:现在还没有用到这个区中的任何页面。
      2). 有剩余空间的碎片区:表示碎片区中还有可用的页面。
      3). 没有剩余空间的碎片区:表示碎片区中的所有页面都被使用,没有空闲页面。
      4). 附属于某个段的区。每一个索引都可以分为叶子节点段和非叶子节点段,除此之外InnoDB还会另外定义一些特殊作用的段,在这些段中的数据量很大时将使用区来作为基本的分配单位。

      这4种类型的区也可以被称为区的4种状态( State ),设计 InnoDB 的大叔们为这4种状态的区定义了特定的名词儿(如下表):

      需要再次强调一遍的是,处于 FREE 、 FREE_FRAG 以及 FULL_FRAG 这三种状态的区都是独立的,算是直属于表空间;而处于 FSEG 状态的区是附属于某个段的。

      状态名含义
      FREE空闲的区
      FREE_FRAG有剩余空间的碎片区
      FULL_FRAG没有剩余空间的碎片区
      FSEG附属于某个段的区

      Caution

      如果把表空间比作是一个集团军,段就相当于师,区就相当于团。一般的团都是隶属于某个师的,就像是处于FSEG的区全都隶属于某个段,而处于FREEFREE_FRAG以及FULL_FRAG这三种状态的区却直接隶属于表空间,就像独立团直接听命于军部一样。

      为了方便管理这些区,设计 InnoDB 的大叔设计了一个称为 XDES Entry 的结构(全称就是Extent DescriptorEntry),每一个区都对应着一个 XDES Entry 结构,这个结构记录了对应的区的一些属性。我们先看图来对这个结构有个大致的了解:


      从图中我们可以看出, XDES Entry 是一个40个字节的结构,大致分为4个部分,各个部分的释义如下:
      Segment ID (8字节) 每一个段都有一个唯一的编号,用ID表示,此处的 Segment ID 字段表示就是该区所在的段。当然前提是该区已经被分配给某个段了,不然的话该字段的值没啥意义。
      List Node (12字节) 这个部分可以将若干个 XDES Entry 结构串联成一个链表,大家看一下这个 List Node 的结构:

      如果我们想定位表空间内的某一个位置的话,只需指定页号以及该位置在指定页号中的页内偏移量即可。所以:a: Pre Node Page Number 和 Pre Node Offset 的组合就是指向前一个 XDES Entry 的指针。 b:Next Node Page Number 和 Next Node Offset 的组合就是指向后一个 XDES Entry 的指针。 把一些 XDES Entry 结构连成一个链表有啥用?稍安勿躁,我们稍后唠叨 XDES Entry 结构组成的链表问题。
      State (4字节) 这个字段表明区的状态。可选的值就是我们前边说过的那4个,分别是: FREE 、 FREE_FRAG 、 FULL_FRAG和 FSEG 。具体释义就不多唠叨了,前边说的够仔细了。
      Page State Bitmap (16字节) 这个部分共占用16个字节,也就是128个比特位。我们说一个区默认有64个页,这128个比特位被划分为64个部分,每个部分2个比特位,对应区中的一个页。比如 Page State Bitmap 部分的第1和第2个比特位对应着区中的第1个页面,第3和第4个比特位对应着区中的第2个页面,依此类推, Page State Bitmap 部分的第127和128个比特位对应着区中的第64个页面。这两个比特位的第一个位表示对应的页是否是空闲的,第二个比特位还没有用。

      • XDES Entry链表 #

        Note

        到现在为止,我们已经提出了五花八门的概念,什么区、段、碎片区、附属于段的区、 XDES Entry 结构吧啦吧啦的概念,走远了千万别忘了自己为什么出发,我们把事情搞这么麻烦的初心仅仅是想提高向表插入数据的效率又不至于数据量少的表浪费空间。现在我们知道向表中插入数据本质上就是向表中各个索引的叶子节点段、非叶子节点段插入数据,也知道了不同的区有不同的状态,再回到最初的起点,捋一捋向某个段中插入数据的过程:

        1. 当段中数据较少的时候,首先会查看表空间中是否有状态为 FREE_FRAG 的区,也就是找还有空闲空间的碎片区,如果找到了,那么从该区中取一些零碎的页把数据插进去;否则到表空间下申请一个状态为 FREE 的区,也就是空闲的区,把该区的状态变为 FREE_FRAG ,然后从该新申请的区中取一些零碎的页把数据插进去。之后不同的段使用零碎页的时候都会从该区中取,直到该区中没有空闲空间,然后该区的状态就变成了FULL_FRAG 。

        现在的问题是你怎么知道表空间里的哪些区是 FREE 的,哪些区的状态是 FREE_FRAG 的,哪些区是FULL_FRAG 的?要知道表空间的大小是可以不断增大的,当增长到GB级别的时候,区的数量也就上千了,我们总不能每次都遍历这些区对应的 XDES Entry 结构吧?这时候就是 XDES Entry 中的 List Node 部分发挥奇效的时候了,我们可以通过 List Node 中的指针,做这么三件事:

          - 把状态为 FREE 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,这个链表我们就称之为 FREE 链表。
          - 把状态为 FREE_FRAG 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,这个链表我们就称之为 FREE_FRAG 链表。
          - 把状态为 FULL_FRAG 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,这个链表我们就称之为 FULL_FRAG 链表。
        

        这样每当我们想找一个 FREE_FRAG 状态的区时,就直接把 FREE_FRAG 链表的头节点拿出来,从这个节点中取一些零碎的页来插入数据,当这个节点对应的区用完时,就修改一下这个节点的 State 字段的值,然后从 FREE_FRAG 链表中移到 FULL_FRAG 链表中。同理,如果 FREE_FRAG 链表中一个节点都没有,那么就直接从 FREE 链表中取一个节点移动到 FREE_FRAG 链表的状态,并修改该节点的 STATE 字段值为FREE_FRAG ,然后从这个节点对应的区中获取零碎的页就好了。

        1. 当段中数据已经占满了32个零散的页后,就直接申请完整的区来插入数据了。

        还是那个问题,我们怎么知道哪些区属于哪个段的呢?再遍历各个 XDES Entry 结构?遍历是不可能遍历的,这辈子都不可能遍历的,有链表还遍历个毛线啊。所以我们把状态为 FSEG 的区对应的 XDES Entry 结构都加入到一个链表喽?傻呀,不同的段哪能共用一个区呢?你想把索引a的叶子节点段和索引b的叶子节点段都存储到一个区中么?显然我们想要每个段都有它独立的链表,所以可以根据段号(也就是 Segment ID )来建立链表,有多少个段就建多少个链表?好像也有点问题,因为一个段中可以有好多个区,有的区是完全空闲的,有的区还有一些页面可以用,有的区已经没有空闲页面可以用了,所以我们有必要继续细分,设计InnoDB 的大叔们为每个段中的区对应的 XDES Entry 结构建立了三个链表:

          - FREE 链表:同一个段中,所有页面都是空闲的区对应的 XDES Entry 结构会被加入到这个链表。注意和直属于表空间的 FREE 链表区别开了,此处的 FREE 链表是附属于某个段的。
          - NOT_FULL 链表:同一个段中,仍有空闲空间的区对应的 XDES Entry 结构会被加入到这个链表。
          - FULL 链表:同一个段中,已经没有空闲空间的区对应的 XDES Entry 结构会被加入到这个链表。
        

        再次强调一遍,每一个索引都对应两个段,每个段都会维护上述的3个链表,比如下边这个表:
        CREATE TABLE t ( c1 INT NOT NULL AUTO_INCREMENT, c2 VARCHAR(100), c3 VARCHAR(100), PRIMARY KEY (c1), KEY idx_c2 (c2) )ENGINE=InnoDB;
        这个表 t 共有两个索引,一个聚簇索引,一个二级索引 idx_c2 ,所以这个表共有4个段,每个段都会维护上述3个链表,总共是12个链表,加上我们上边说过的直属于表空间的3个链表,整个独立表空间共需要维护15个链表。所以段在数据量比较大时插入数据的话,会先获取 NOT_FULL 链表的头节点,直接把数据插入这个头节点对应的区中即可,如果该区的空间已经被用完,就把该节点移到 FULL 链表中。

      • 链表基节点 #

        Note

        上边光是介绍了一堆链表,可我们怎么找到这些链表呢,或者说怎么找到某个链表的头节点或者尾节点在表空间中的位置呢?设计 InnoDB 的大叔当然考虑了这个问题,他们设计了一个叫 List Base Node 的结构,翻译成中文就是链表的基节点。这个结构中包含了链表的头节点和尾节点的指针以及这个链表中包含了多少节点的信息,我们画图看一下这个结构的示意图:


        我们上边介绍的每个链表都对应这么一个 List Base Node 结构,其中:
        1). List Length 表明该链表一共有多少节点,
        2). First Node Page Number 和 First Node Offset 表明该链表的头节点在表空间中的位置。
        3). Last Node Page Number 和 Last Node Offset 表明该链表的尾节点在表空间中的位置。
        一般我们把某个链表对应的 List Base Node 结构放置在表空间中固定的位置,这样想找定位某个链表就变得soeasy啦。

      • 链表小结 #

        Note

        综上所述,表空间是由若干个区组成的,每个区都对应一个 XDES Entry 的结构,直属于表空间的区对应的 XDESEntry 结构可以分成 FREE 、 FREE_FRAG 和 FULL_FRAG 这3个链表;每个段可以附属若干个区,每个段中的区对应的 XDES Entry 结构可以分成 FREE 、 NOT_FULL 和 FULL 这3个链表。每个链表都对应一个 List Base Node 的结构,这个结构里记录了链表的头、尾节点的位置以及该链表中包含的节点数。正是因为这些链表的存在,管理这些区才变成了一件so easy的事情。

    • 段的结构 #

      我们前边说过,段其实不对应表空间中某一个连续的物理区域,而是一个逻辑上的概念,由若干个零散的页面以及一些完整的区组成。像每个区都有对应的 XDES Entry 来记录这个区中的属性一样,设计 InnoDB 的大叔为每个段都定义了一个 INODE Entry 结构来记录一下段中的属性。大家看一下示意图:


      它的各个部分释义如下:
      Segment ID 就是指这个 INODE Entry 结构对应的段的编号(ID)。
      NOT_FULL_N_USED 这个字段指的是在 NOT_FULL 链表中已经使用了多少个页面。下次从 NOT_FULL 链表分配空闲页面时可以直接根据这个字段的值定位到。而不用从链表中的第一个页面开始遍历着寻找空闲页面。
      3个 List Base Node分别为段的 FREE 链表、 NOT_FULL 链表、 FULL 链表定义了 List Base Node ,这样我们想查找某个段的某个链表的头节点和尾节点的时候,就可以直接到这个部分找到对应链表的 List Base Node 。so easy!
      Magic Number 这个值是用来标记这个 INODE Entry 是否已经被初始化了(初始化的意思就是把各个字段的值都填进去了)。如果这个数字是值的 97937874 ,表明该 INODE Entry 已经初始化,否则没有被初始化。(不用纠结这个值有啥特殊含义,人家规定的)。
      Fragment Array Entry 我们前边强调过无数次段是一些零散页面和一些完整的区的集合,每个 Fragment Array Entry 结构都对应着一个零散的页面,这个结构一共4个字节,表示一个零散页面的页号。

      结合着这个 INODE Entry 结构,大家可能对段是一些零散页面和一些完整的区的集合的理解再次深刻一些。

    • 各类型页面详细情况 #

      到现在为止我们已经大概清楚了表空间、段、区、XDES Entry、INODE Entry、各种以 XDES Enty 为节点的链表的基本概念了,可是总有一种飞在天上不踏实的感觉,每个区对应的 XDES Entry 结构到底存储在表空间的什么地方?直属于表空间的 FREE 、 FREE_FRAG 、 FULL_FRAG 链表的基节点到底存储在表空间的什么地方?每个段对应的 INODE Entry 结构到底存在表空间的什么地方?我们前边介绍了每256个连续的区算是一个组,想解决刚才提出来的这些个疑问还得从每个组开头的一些类型相同的页面说起,接下来我们一个页面一个页面的分析,真相马上就要浮出水面了。

      • FSP_HDR 类型 #

        Note

        首先看第一个组的第一个页面,当然也是表空间的第一个页面,页号为 0 。这个页面的类型是 FSP_HDR ,它存储了表空间的一些整体属性以及第一个组内256个区的对应的 XDES Entry 结构,直接看这个类型的页面的示意图:


        从图中可以看出,一个完整的 FSP_HDR 类型的页面大致由5个部分组成,各个部分的具体释义如下表:
        File Header 和 File Trailer 就不再强调了,另外的几个部分中, Empty Space 是尚未使用的空间,我们不用管它,重点来看看 File Space Header 和 XDES Entry 这两个部分。

        名称中文名占用空间大小简单描述
        File Header文件头部38 字节页的一些通用信息
        File Space Header表空间头部112 字节表空间的一些整体属性信息
        XDES Entry区描述信息10240 字节存储本组256个区对应的属性信息
        Empty Space尚未使用空间5986 字节用于页结构的填充,没啥实际意义
        File Trailer文件尾部8 字节校验页是否完整
    • Segment Header 结构的运用 #

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

  • 系统表空间 #

    • 系统表空间的整体结构 #

    • 总结图 #


comments powered by Disqus