扒一扒MySQL的InnoDB存储引擎

在讲之前,我先抛几个问题

  • MYSQL的记录是怎么存储的?
  • 页内记录到底是怎么维护的?
  • 页内查询过程是怎样的?

1.Page页面

MYSQL管理数据库的一个单位叫Page页面,数据都是存在页面里的。那我们想要知道数据是怎么存的,就需要了解页长什么样子
在这里插入图片描述
我们来盘点一下:

  • 页头(Page Header):存一些统计信息,记录页面的控制信息,共占56字节,包括页使用情况,页的左右兄弟页面指针(这个就是双向链表,把左右兄弟页面的指针给拿到)等
  • 虚记录:分为最大虚记录和最小虚记录。它两把这页里存储的数据的范围给框住了。那怎么比较谁大谁小?用的是主键去比较:最大虚记录比页内最大主键还大,最小虚记录比页内最小主键还小。
  • 记录堆:这部分就是存储记录的区域,分为有效记录和已删除的记录。已被删除的记录构成一个链表,叫做自由空间链表。如图蓝色的是已经被删除的记录,用一个链表把他们连起来。
  • 未分配空间:页面未使用的存储空间,除了用了一部分的橙色的区域和已删除的蓝色数据,剩下的就是未分配空间了,后面有新的数据插入,往里放就行了。
  • slot:这一块对数据检索非常有用,后面详说
  • 页尾:页面的最后部分,占8个字节,主要存储页面的校验信息。这一页如果写坏了,数据不对了,通过校验位就可以检查出来

2.聚簇索引

主键是怎么存的呢?又是按什么顺序存储的呢?实际上说的是页里面数据是怎么组织起来的,还有就是插入数据的策略,怎么插入数据,还有就是页内的查询是怎样的?

我们先来看看聚簇索引:
在这里插入图片描述
首先,聚簇索引是一颗B+树,那什么是聚簇呢?

图中下面绿色的部分是数据区域,数据是基于紫色部分的主键顺序存储的,数据和主键存到一起,主键是按照树的顺序去组织的,这个结构叫聚簇。

我们再看每一个Page,Page里面有最大虚记录,最小虚记录。那这个数据再里面到底是怎么存的?或者说,数据的顺序是怎么保证的?到底是物理有序,还是逻辑有序?

我们知道——物理有序写入不友好,查询友好;逻辑有序查询不友好,写入友好。

先看下面这幅图,思考一下插入主键为10,9,8的数据,是按物理有序存储还是逻辑有序存储:
在这里插入图片描述
物理有序:一开始先插一个10,9要插进来先移动10;8再想插进来,9和10都要移动把8插进去(流鼻血.gif)

逻辑有序:插入10,再插入9,9的指针指向10,再插入8,8的指针指向9。

注意,这跟有没有索引没有关系,索引是基于它上层的数据结构。

那如果是我们自己去设计一个数据库的话,要怎么优化读写性能呢?

数据插入是写入IO,数据查询是读IO,不管是写还是读,在分析存储的时候,无非是这四种:顺序写、随机写、顺序读、随机读。如果是顺序写,数据会有各种移动,写入性能肯定非常糟糕。但是没办法,优化写入的手段十分有限,不过呢我们却有很多办法优化读。

所以想都不用想,页内数据存储的顺序就是逻辑有序。

重新梳理一下,Page与Page之间由双向链表连接,页内是用小的链表连起来的:
在这里插入图片描述
我们再来重新画一下这棵树:
在这里插入图片描述
这里要注意,树的每个节点,它也是一个Page。既然是个Page,也会有页头,也会有双向链表

接下来我们分析一下它的插入策略

蓝色部分已删除的空间(记录堆)怎么办呢?我们得想办法尽量把它们利用上,这个换谁做数据库设计都要这么设计。

其实,插入策略就是先使用自由空间链表,再使用未使用空间,把数据库“空洞”给补上。不过呢,自由空间链表的空间也不能完全利用上,比如旧的数据占25个字节,新的数据假设都只有20个字节,那剩下这5个字节基本也利用不上,这样一来就会产生越来越多的“碎片”。经过长时间的插入删除插入删除以后,我们就得考虑给数据库做一次收缩,比如通过两次主从表的双向同步,把所有表数据重新插一遍。

3.slot槽

页内的数据是便利还是二分查找?

无论数据是物理连续还是逻辑有序,都不能二分查找,都得用遍历的方法。如果我们设置一款数据库,通过索引找到数据再哪个Page里面,要是Page这一层通过遍历方式,那效率实在是太低了,所以数据库肯定不能这样设计。

遍历不行,那就使用二分查找吧,提高一下效率,那MYSQL是怎么做的呢?
在这里插入图片描述
如图,最小虚记录和最大虚记录之间形成一个链表,这时候slot区就派上用场了,每个solt槽指向链表中的某一个位置,每个槽的大小一样,可以理解为一个指针,这样我们只需要用一个算法把每个子链表的长度拆成差不多大小就行了。

在查找的时候,先基于Sn、S0找到指向的最大最小虚记录,在Slot区进行二分:先找到Sn和S0的中间位置,中间找到某个Slot,然后再一步步进行比较,通过几次二分后找到具体的子链表,最后,在子链表内进行遍历找到最终的记录。这样我们借助Slot区实现了一个近似二分查找的方法。这特别像Java里面的跳表结构,一次查找跳一次,再一次查找再跳一次,效率就特别高了。

最后我们来总结一波:一开始我们讲了Page页面的结构是怎样的,有页头页尾、最大最小虚记录、记录堆、未分配空间和自由空间链表、Slot区,知道了他们各自的作用是什么;接着通过聚簇索引这棵B+树把所有Page连接起来,Page与Page之间是逻辑有序的,已删除的记录用自由空间链表把它们连接起来,插入的时候优先把已删除数据的“空洞”给填上;页内的数据通过Slot槽划分一个个子链表,查询的时候比较Slot槽指向的数据,像二分法一步步确定数据在哪个子链表,最后在子链表内进行遍历。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值