32.磁盘数据页的分裂与合并

页的分裂

上回我们讲到了数据页的物理存储结构,数据页之间是组成双向链表的,数据页内部的数据行是组成单向链表的,每个数据页内根据主键做了一个页目录

然后一般来说,你没有索引的情况下,所有的数据查询,其实在物理层面都是全表扫描,依次扫描每个数据页内部的每个数据行。

上述描述,其实就是没有索引情况下在一个表中的数据查询情况,这个速度可以说是慢到惊人,所以一般肯定是不能让查询走全表扫描的。因此正常在数据库中的查询,必须要运用到索引来加速查询的执行。

在一个表里不停的插入数据的时候,会涉及到一个页分裂的过程,也就是说,这个表里是如何出现一个又一个的数据页的。

大家都知道,正常情况下我们在一个表里插入一些数据后,他们都会进入到一个数据页里去,在数据页内部,他们会组成一个单向链表,这个数据页内部的单向链表大致如下所示,我们看看

image.png

大家看上面的图,里面就是一行一行的数据,刚开始第一行是个起始行,他的行类型是2,就是最小的一行,然后他有一个指针指向了下一行数据,每一行数据都有自己每个字段的值,然后每一行通过一个指针不停的指向下一行数据,普通的数据行的类型都是0,最后一行是一个类型为3的,就是代表最大的一行。

上面就是一个典型的数据页内部的情况,那么今天要讲的页分裂是什么意思呢?

是这样的,假设你不停的在表里插入数据,那么刚开始是不是就是不停的在一个数据页插入数据?接着数据越来越多,越来越多,此时就要再搞一个数据页了,如下图。

image.png

但是此时会遇到一个问题,后续我们会讲到索引这块机制,索引运作的一个核心基础就是要求你后一个数据页的主键值都大于前面一个数据页的主键值,但是如果你的主键是自增的,那还可以保证这一点,因为你新插入后一个数据页的主键值一定都大于前一个数据页的主键值。

但是有时候你的主键并不是自增长的,所以可能会出现你后一个数据页的主键值里,有的主键是小于前一个数据页的主键值的。

比如在第一个数据页里有一条数据的主键是10,第二个数据页里居然有一条数据的主键值是8,那此时肯定有问题了。

所以此时就会出现一个过程,叫做页分裂,就是万一你的主键值都是你自己设置的,那么在增加一个新的数据页的时候,实际上会把前一个数据页里主键值较大的,挪动到新的数据页里来,然后把你新插入的主键值较小的数据挪动到上一个数据页里去,保证新数据页里的主键值一定都比上一个数据页里的主键值大。

大家看下图,假设新数据页里,有两条数据的主键值明显是小于上一个数据页的主键值的,如图所示。

image.png

如上图所示,第一个数据页里有1、5、6三条数据,第二个数据页里有2、3、4三条数据,明显第二个数据页里的数据的主键值比第一个数据页里的5和6两个主键都小,所以这个是不行的。

此时就会出现页分裂的行为,把新数据页里的两条数据挪动到上一个数据页,上一个数据页里挪两条数据到新数据页里去,如下图所示。

image.png

所以上述就是一个页分裂的过程,核心目标就是保证下一个数据页里的主键值都比上一个数据页里的主键值要大,也就是要保证顺序性。

说到这里又不得不提一下uuid,无论是主键索引、唯一索引、还是普通索引都不建议使用uuid作为这些索引的值,因为uuid本身是无序的,在插入的过程中会导致频繁的数据页的分裂与合并。

页合并

image.png B+树为了维护索引有序性,在插入新值的时候需要做必要的维护。以上面这个图为例,如果插入新的行ID值为700,则只需要在R5的记录后面插入一个新记录。如果新插入的ID值为400,就相对麻烦了,需要逻辑上挪动R3后面的数据,空出位置。

当发生数据大量的删除操作后,为了平衡,Mysql默认当数据页的数据小于 2/m就会进行页合并【m的配置的多路的路数】。其实不仅仅删除操作,大量的增删改也一样,都能照成大量的数据页空洞(即表空间浪费),比如当新增了多条数据后当前的数据页 大于 15/16m 数,就会进行页分裂。新增操作时,Mysql会预留 1/16 的空间给后续的写操作,否则后续只要有一点表动就会触发页分裂。

而更糟的情况是,如果R5所在的数据页已经满了,根据B+树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。

除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率降低大约50%。

当然有分裂就有合并。当相邻两个页由于删除了数据,利用率很低之后,会将数据页做合并。合并的过程,可以认为是分裂过程的逆过程。

页连接方式:指针

如果插入的数据是在主键树叶子结点的中间,后面的所有页如果都是满的状态,是不是会造成后面的每一页都会去进行页分裂操作,直到最后一个页申请新页移过去最后一个值?

只会分裂它要写入的那个页面。每个页面之间是用指针串的,改指针就好了,不需要“后面的全部挪动

优先插入前后页:减少页分裂

如果是在某个数据满了的页的首尾插入数据,为了减少数据移动和页分裂,为了增加空间利用率,提高性能。会先去前后两个页看看是否满了,如果没满会先将数据放到前后两个页上。

参考:https://time.geekbang.org/column/intro/139 参考:https://blog.csdn.net/zht245648124/article/details/129271347

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值