Skip to content

区的概念

我们知道真实记录是存储在数据页中,如果表中的数据太大,导致表空间中会有很多的页,为了很好的管理这些页,所以又设计了区,每个数据页是16kb,连续的64个页就构成了1个区, 所以1个区的大小是 1M; 表空间就是由若干个区组成的,每256个区被划分为一组。

为什么要引入区的概念? 存放记录的数据页之间是通过双向链表链接的,索引中存储目录项的页之间也是通过双向链表链接的,如果两个数据页在物理上离的很远导致,在查询的时候磁头移动的位置很大从而影响性能, 所以才引入了区,区是一片连续的空间,这样可以避免很多的随机IO;每次需要分片新的数据页面时就需要从区从申请;

段的概念

我们想象一下,在执行B+树查询的时候,如果不区分叶子节点和非叶子节点,所有的节点所代表的页都存放在一起,那么同样会出现很多随机IO影响性能,为了解决这个问题,又引入了段。 存放叶子节点区的集合就称为段,存放非叶子节点的区集合也是一个段。默认情况下创建一个表只有聚簇索引,那么它也有又两个段。

一个段至少一个区,一个表至少两个段,那么一个表最小也要占用2M,这样对于一下小表来说是不是有点太浪费空间了;所以innodb又引入了碎片区的概念,碎片区存放的数据页可以来自不同 的段,当某个段已经占用了32个碎片区页面之后,他就会自己申请独属于自己段的区,已经占用的碎片区中的页不会重新拷贝。

区的分类

  • free: 空闲的区,这个区中没有任何使用的页
  • fee_frag: 有剩余空闲页面的碎片区
  • full_frag: 没有剩余空闲页面的碎片区
  • fseg: 独属于某个段的区

为了管理好不同类型的区,innodb设计了 xdes_entry 的结构,每个区都对应了一个xdes_entry, 总共占用40个字节,由4部分组成:

  • segmentId: 每个段都有个独立的ID,该值表示这个区属于某个段
  • list node: 简单理解称两个指针,分别执行前一个区,与后一个区;可以看出区是由list node构成了一条双向链表
  • state: 这表示区的状态,可选状态:free, fee_frag, full_frag, fseg
  • page state bitmap: 占用16字节,也就是128位;一个区默认由64个页,所以每个页对应2位,其中第一位表示当前是否空闲,第二位还没有使用到。

当某个段中数据较少的时候,插入数据时,先从表空间中检查是否有状态为fee_frag的区,如果找到了就从该区中取一个页面把数据插入进去,否则从表空间中申请一个状态 为 free的区,把该区变成了一个fee_frag,然后再从新区中取一个页面插入数据。之后不同的段使用零散页都从这个区中取,直到没有了空闲页就把这个区变成full_frag。

新的问题来了,我们如何知道表空间中哪些区是free、哪些区是full_frag,这时候就需要xdes_entry的list node,表空间中存放了三条链表,这三条链表链接了不同状态的区:

  • free 链:空闲区链表
  • free_frag 链:有剩余空闲页的区链表
  • full_frag 链:没有剩余空间的区链表

当段已经占用了32个零散页后,段就会申请独属于字节的区,这时候也会建立和表空间类似的三条链表:free链 、 free_frag链 、 full_frag链

段的结构

每个段都定义了一个 inode_entry 结构

  • segmentId: 记录段id
  • not_full_n_used: not_full链已经使用了多少个页面
  • 3个list base node: 分别为段的free链表,not_free链表,full链表,记录这三个链表的表头

原文链接: http://herman7z.site