美文网首页MySQL
84-存放页的大池子-InnoDB的表空间

84-存放页的大池子-InnoDB的表空间

作者: 紫荆秋雪_文 | 来源:发表于2023-02-28 10:44 被阅读0次

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

    一、页

    1.1、页类型

    页类型.png

    1.2、页通用部分

    image.png

    1.3、页的File Header File Header.png

    二、区

    对于16KB的页来说,连续的64个页就是一个。256个算是第一个组

    2.1、表空间 表空间.png

    2.2、表空间中的页

    image.png

    2.3、碎片(fragment)区

    为了考虑以完整的区为单位分配给某个对于数据量较小的表太浪费存储空间的这种情况,设计InnoDB的大佬们提出了一个碎片(fragment)区的概念,也就是在一个碎片区中,并不是所有的页都是为了存储同一个段的数据而存在的,而是碎片区中的页可以用于不同的目的,比如有些页用于段A有些页用于段B有些页甚至哪个段都不属于。碎片区直属于表空间,并不属于任何一个段

    • 某个段分配存储空间的策略是这样的
      • 第一步:在刚开始向表中插入数据的时候,是从某个碎片区单个页为单位来分配存储空间的
      • 第二步:当某个段已经占用了32个碎片区页之后,就会以完整的区为单位来分配存储空间

    2.4、区的分类

    处于FREE、FREE_FRAG以及FULL_FRAG这三种状态的区都是独立的,算是直属于表空间;而处于FSEG状态的区是附属于某个段

    image.png

    2.5、XDES Entry的结构

    为了方便管理这些区,设计InnoDB的大佬设计了一个称为XDES Entry的结构(全称就是Extent Descriptor Entry),每一个都对应着一个XDES Entry结构,这个结构记录了对应的区的一些属性

    XDES Entry的结构.png

    2.6、 XDES Entry链表

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

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

    • 1、把状态为FREE的区对应的XDES Entry结构通过List Node来连接成一个链表,这个链表我们就称之为FREE链表
    • 2、把状态为FREE_FRAG的区对应的XDES Entry结构通过List Node来连接成一个链表,这个链表我们就称之为FREE_FRAG链表
    • 3、把状态为FULL_FRAG的区对应的XDES Entry结构通过List Node来连接成一个链表,这个链表我们就称之为FULL_FRAG链表
    2.6.2、当段中数据已经占满了32个零散的页后,就直接申请完整的区来插入数据了

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

    2.6.3、小结

    InnoDB表,一个聚簇索引,所以共有2个段(叶子段和非叶子段)每个段都会维护上述3个链表,总共是6个链表,加上我们上面说过的直属于表空间的3个链表,整个独立表空间共需要维护9个链表。所以段在数据量比较大时插入数据的话,会先获取NOT_FULL链表的头节点,直接把数据插入这个头节点对应的区中即可,如果该区的空间已经被用完,就把该节点移到FULL链表中

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

    2.7、链表基节点

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

    List Base Node结构.png
    • List Length表明该链表一共有多少节点

    • First Node Page NumberFirst Node Offset表明该链表的头节点在表空间中的位置

    • Last Node Page NumberLast Node Offset表明该链表的尾节点在表空间中的位置

    • 一般我们把某个链表对应的List Base Node结构放置在表空间中固定的位置,这样想找定位某个链表就变得so easy啦

    2.8、 链表小结

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

    四、段

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

    3.1、段结构 image.png

    相关文章

      网友评论

        本文标题:84-存放页的大池子-InnoDB的表空间

        本文链接:https://www.haomeiwen.com/subject/pobhldtx.html