MySQL-段、区、组

如果我们有大量的记录,那么表空间中页的数量就会非常的多,为了更好的管理这些页,设计者又提出了区的概念。对于16KB的页来说,连续的64个页(连续的)就是一个区,也就是说一个区的大小为1MB。(256个区被划分成一个组)

为什么需要区

通过页其实已经形成了完整的功能,我们查询数据时这样沿着双向链表就可以查到数据,但是页与页之间在物理位置上可能不是连续的,如果相隔太远,那么我们从一个页移动到另一个页的时候,磁盘就要重新定义磁头的位置,产生随机IO,影响性能,所以我们才要引入区的概念,一个区就是物理位置连续的64个页。区是属于某一个段的(或者是混合)。

什么是段

正常情况下,我们检索都是在叶子节点的双向链表进行的,也就是说我们会把区进行区分,如果不区分把叶子节点和非叶子结点混在一起,那么效果就会打大折扣,所以对于一个索引B+树来说,我们区别对待叶子节点的区和非叶子节点的区,并把存放叶子节点的区的集合称为一个段把存放非叶子节点区的集合也称为一个段,所以一个索引会生成两个段:叶子节点段和非叶子节点段。

碎片区

默认情况下,假如我们新建一个索引就会生成两个段(叶子节点段和非叶子节点段),而一个段中至少包含一个区,也就是需要2MB的空间,假如我们这个表压根没有多少数据,那么一次就要申请2MB的空间明显是浪费的。为了解决这个问题,设计者提出了碎片区的概念,碎片区中的页可能属于不同的段,也可以用于不同的目的,至于如何控制应不应该给一个段申请专属的区,会进行以下控制:

  1. 刚开始向表插入数据,都是从某一个碎片区以页为单位来分配存储空间。
  2. 当一个段占用的空间达到了32个碎片区的页之后,就会开始给这个段申请专属的区。

区分类

我们现在知道表空间是由若干个区组成的,这些区可以分成以下的4中类型:

  1. FREE:没有用到这个区的任何一个页面。
  2. FREE_FRAG:有剩余空闲页的碎片区。
  3. FULL_FRAG:没有剩余空闲页的碎片区。
  4. FSEG:专属某一个段的区。

XDES Entry

为了管理各种各样的区,设计者提成了一个XDES Entry结构。
MySQL-段、区、组

  1. Segment ID:每一个段都有自己的编号,如果一个XDES Entry属于一个段,就是所在段的编号,如果是碎片区,则这个属性没有意义。
  2. State:四种类型的标识。
  3. Page State Bitmap:16字节,128位,每两位对应一个区中的页。第一位标识是否空闲,第二位没有用到。
  4. List Node:若干个XDES Entry形成链表。

XDES Entry链表

当我们需要某一个类型的XDES Entry时候,如何快速的拿到,这就跟XDES Entry形成的链表有关了,对于属于表的三种类型 FREE,FREE_FRAG,FULL_FRAG,会生成三个链表:

  1. 当我们插入一条新记录时,如果FREE_FRAG链表不为空,在FREE_FRAG链表中拿到一个XDES Entry并取得一个页添加数据。如果FREE_FRAG链表为空,在FREE链表拿到一个XDES Entry并取得一个页添加数据,之后把XDES Entry移动到FREE_FRAG链表里。
  2. 如果使用FREE_FRAG链表中的XDES Entry是发现满了,就把XDES Entry移动到FULL_FRAG链表里。

FSEG区

我们前面说了FREE,FREE_FRAG,FULL_FRAG三种类型的链表,这三种类型是针对碎片区的,而对于段专属的区类型FSEG,也会形成三种类型的链表,这三个链表属于段,存在段结构中。

  1. FREE:属于同一个段的空闲FSEG区会形成这个链表。
  2. NOT_FULL:属于同一个段的还有空闲页的FSEG区会形成这个链表。
  3. FULL:属于同一个段的没有空闲页的FSEG区会形成这个链表。

段结构

段不是一个物理上连续的空间,而是一个逻辑上的概念,一个段包含若干零散的页面还有自己专属的FSEG区,这些FSEG区形成FREE, NOT_FULL, FULL三种链表保存在段上,像XDES Entry一样,设计者也给段定义了一个结构INODE Entry。
MySQL-段、区、组

  1. Segment ID:该段(INODE Entry结构)的编号ID。
  2. NOT_FULL_N_USED:NOT_FULL链表中使用了多少个页面。
  3. List Base Node:是INODE Entry给FSEG链表定义的一个结构,通过这个结构可以直接找到链头,链尾。
  4. Magic Number:表示这个INODE Entry是否已经被初始化。
  5. Fragment Array Entry:前面说过只有使用了32个碎片区的页之后,才会开始申请专属的FSEG区,而32个碎片页就是靠这个属性来定位的。
    段是一些零散页和完整区的结合。

区存放在哪

XDES Entry保存了有关区的消息,大小为40字节,而我们说一个区大小是1MB,由64个页组成,16KB * 64 = 1024KB 刚好1MB,也就是没有额外的40字节来存储XDES Entry了,那这些XDES Entry到底是存放在哪的?
前面我们有说过组的概念,组由256个区组成。表空间由组组成,组中包含了XDES Entry等信息。
MySQL-段、区、组
以上我们可以理解为一个表空间的组成,有组1,组2,我们之前所说的各种信息,都会保存在每一组的第一个区中的前面几个页里面。而作为表空间的第一个组,则要另外保存与表相关的信息。

  • 表空间第一个组第一个区:第一页FSP_HDR页,第二页IBUF_BITMAP页,第三页INODE页
  • 其他组第一个区:第一页XDES页,第二页IBUF_BITMAP页

FSP_HDR

MySQL-段、区、组

  • File Space Header:存储表相关的信息,比如我们之前说的属于表的三个链表(FREE,FREE_FRAG,FULL_FRAG)就会存储在这个区域。
  • XDES Entry 0 - XDES Entry 255:该组下的256个XDES Entry结构。

XDES

与FSP_HDR页一样,只是没有File Space Header。
MySQL-段、区、组

IBUF_BITMAP

当发生记录的增删改时,聚餐索引会发生相应的变化,如果也二级索引的B+树也要发生相应的调整,而这些对应的页往往都不在内存中,如果要修改必须发生磁盘IO,把对应的二级索引页也给读到内存中进行修改,这样就增加了IO的次数,影响性能。
IBUF_BITMAP页的作用就是缓存这些操作,等到下一次MySQL刚好把这些要修改的页读取到内存的时候,顺便修改。

INODE

我们之前分析过,一个索引有两个段,一个段会对应INODE Entry结构,这个页就是用来保存这个表空间所有的INODE Entry结构的。

上一篇:面试官:Redis新版本开始引入多线程,谈谈你的看法?


下一篇:electron-vue:Vue.js 开发 Electron 桌面应用