页分裂和页合并


mysql表数据组成

一个表通常由一个.ibd文件所表示,一个文件又由N个段组成,每个段关联一个索引.一个段又由多个区组成,而每个区又由多个页组成,默认情况下,区的大小为1MB,页的大小为16KB,一个区中最多可以存放64个页.一个页至少需要存放两行数据,因此页中的行数据的长度最长为8KB,而每行数据有多大,由表的模式所决定,通常表的模式又由一个.frm文件所表示.一张图片可以更形象的表达一些概念:

img

页的组成

Mysql中操作数据的最小单位不是行,而是页,这意味着Mysql不会单独从磁盘上查询一行记录,而是至少查到一个页,然后从中获取行记录.在主键索引的叶子节点中,数据将按照主键大小按顺序存放在页中,页和页之间通过双向链表进行连接,这也就意味着在物理上相邻的两个页不一定是按顺序的.我们所讨论的页分裂和页合并中的”页”具体指的就是主键索引的叶子节点.

页的内部

一个具体的页的样子如下图,主键ID依次为1,2,3,4,通常页中有一个50%的阈值用于合并操作,也就是页大小的一半.

img

数据将按顺序插入页中:

img

如果数据无法存放在当前页,那么将存放在下一个页中

img

页合并

如果页中的数据被删除,那么实际上这块的空间并不会被回收,而是标记为可重复利用.当一个页的数据被删除或者更新,空间小于所规定的阈值大小,那么Mysql会查找前一个页,和后一个页,判断是否可以将这个页合并到另外一个页,这样就可以节省下一个页的空间.以下是Page5和Page6的一个合并过程:

  1. Page5的一些数据被删除,并且空间降到了阈值之下

img

  1. Page5的下一个页Page6的数据此时不到一半,并且这些数据可以放入Page5

img

  1. 进行页合并操作

img

  1. Page6变成了空页,用于存放新的数据

img

页分裂

如果一个页快满了,此时我们插入数据,但下一个页的空间也全部占满.这个时候Mysql将创建一个新页,然后将快满的这个页的部分数据迁移到新页中,这部分数据就是超出原来那个页阈值的那部分数据,之后再插入新的数据.以下是一个页分裂过程:

  1. 往Page10插入数据,但此时该页已没有空间可以容纳

img

  1. Page10的下一个页Page11也没有空间可以容纳,并且27这个数据是要插入到28之前的.

img

  1. 此时创建一个新页Page12,将Page10的24-26号数据迁移到新页,并插入27

img

img

  1. 修改页之间的链接关系,Page10的下一个页此时是Page12而不是Page11,因此页并不是物理连续的,而是逻辑连续的.两个相邻的页可能存放在不同的区中.

索引设计带来的影响

假如我们有个主键,如果我们使用自增id,那么产生的页分裂和页合并会比较少,但是如果我们使用无规则的id,那么大概率会使得整个数据分布得很稀疏,进而给性能带来以下影响:

  • 使用了更多的页,浪费更多的磁盘空间
  • 数据分布在很多稀疏的页中,加载页也需要CPU时间,那么可能导致获取数据时间变长
  • 页合并和页分裂进行过程会带来性能影响

参考资料

https://www.percona.com/blog/innodb-page-merging-and-page-splitting/


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!