(MySql) InnoDB索引的本质和快速查询过程

本文涉及的范围包括:
1:到底什么是InnoDB引擎的索引,它的本质是什么,是如何实现的,实现的思路是什么?
2:根据索引的实现思路,当我们要查询一条数据(行记录)时,查询语句的查询过程是什么?

说到数据库引擎的索引,我们都知道它的作用是提高数据的查询速度,即在成千上万条记录中,如何快速的找到我们需要的数据?要搞清楚这个过程,首先我们需要搞懂,我们插入到数据库中的数据是如何存储和表示的

InnoDB行格式:一条数据是如何存储的
InnoDB定义了4中行格式:COMPACT行格式,Redundant行格式,Dynamic和Compressed行格式。这些行格式大同小异,都是用来便捷的表示一行数据。我们以COMPACT行格式为例。

在COMPACT行格式中,一条数据包括两部分信息:记录的额外信息记录的真实数据。
在这里插入图片描述

记录的真实数据就是我们插入的记录的每一列的数据,额外信息包括:
1.变长字段长度列表:若某一个字段(列)的数据类型的长度数可变的,则记录它的真实长度值(字节数)。
2.NULL值列表:若某一个字段(列)的数据可以为NULL,则用一个位表示它是否为NULL。
3.记录头信息
在这里插入图片描述
其中比较重要的有:
delete_mask:标记改数据是否被删除
n_owned:表示一组中有多少条记录(后面会说到组的概念)
record_type:该记录的类型
next_recod:下一条记录的相对位置,可以理解为指向下一条记录的指针

页:InnoDB管理存储空间的基本单位。

当我们选用InnoDB作为数据库表的引擎时,我们创建表并向表中插入一些数据后,当我们关闭数据库,再打开,我们的数据是不会丢失的。这是因为MySql将我们的数据存储在了本地的文件系统中,可以进行持久化的保存。
而在进行数据库的操作时,我们对数据的增删改查操作都需要在内存中进行,所以我们首先需要将数据加载进内存,然后在将修改后的数据刷新到磁盘(文件)。在这个数据交互的过程中,数据量的最小单位为 ,一页的大小一般是16KB。
它也是InnoDB管理存储空间的基本单位。我们插入的数据都存放在一个一个的页中。

InnoDB数据页代表的16KB大小的存储空间被划分为多个部分:
在这里插入图片描述
各个部分的功能如下:
在这里插入图片描述
Infimum + supremum + User Records 是 中的行记录, Infimum 和 supremum 行记录是系统创建的 最小记录行 和 最大记录行(类似于头结点和尾节点)。行记录在页中是按照主键从大到小的顺序排列的,所以我们可以将 页 中的行记录看成是一个按照主键从小到大的顺序形成的单链表。

在这里插入图片描述
当我们在递增的单链表中查找某条记录时,我们任然需要重头遍历一遍链表,速度很慢。那么我们如何才能对递增的单链表进行快速的查找呢? 单链表虽然是有序的但是由于地址空间不是连续的,因此无法进行二分查找。

我们可以将单链表上的记录分为若干组,将每组主键的最大值记录的偏移量(槽)提取出来,放在一个数组中。因此它们然后是有序的,所以我们可以在这个数组上进行二分查找,快速的找到我们需要的组。而这个数组就是 Page Directory 页目录。但表中有更多的数据后,它的结构如下:
在这里插入图片描述
所以在一个数据页中查找指定主键值的记录的过程分为两步:
1:通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录。
2:通过记录的next_record属性遍历该槽所在的组中的各个记录。

B+树:索引
在实际开发过程中,我们数据表中的数据是非常非常多的,会被记录在很多的页中。这些页按照主键的递增排列,组成一个双向链表。如下图:橙色是主键字段,蓝色和红色是非主键字段。

在这里插入图片描述
因为这些16KB的页在物理存储上可能并不挨着,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做个目录,每个页对应一个目录项,每个目录项包括下边两个部分:

1.页的用户记录中最小的主键值,我们用key来表示。
2.页号,我们用page_no表示。
所以我们为上边几个页做好的目录就像这样子:
在这里插入图片描述
这个目录其实就是索引。InnoDB索引也是被存在 页 中的行记录,只是它的头信息的 record_type = 1。
在这里插入图片描述
当数据非常非常多时,往往一层索引是不够的,最终会形成多层索引。最上层的索引页就是B+树的根节点,通过不断往上抽取,最上层的页节点一定为1。

根据主键信息,在索引中快速查询数据的过程:
1:在根节点(页)中利用 页目录 信息,二分查找找到满足条件组,在组内链表上遍历找到满足条件的行记录。若行记录 record_type = 1表示行记录是 目录记录,在根据该行记录的 页号 找到下层对应的页。
2:在页中进行1 同样的操作,直到 record_type = 0,表示 到达了叶子节点, 页中行记录为 用户记录
3:在页节点页中利用 页目录 信息,二分查找找到满足条件组,在组内链表上遍历找到满足条件的行记录。

例子:我们在下面例子中找主键为20的记录,假设组的大小为1。
在这里插入图片描述
1:首先在根节点(页30)中利用 页目录信息二分查找,找到主键值小于等于20的最大记录,即“1 1 30”的记录。record_type = 1 继续向下。
2:在页30 中找到主键值小于等于20的最大记录,即“1 12 9”的记录。record_type = 1 继续向下。
3:在页 9 中找到主键值小于等于20的最大记录,即“0 20 2 e”的记录。record_type = 0 已经到达叶子节点, 页找到了我们需要的记录。

因此我们可以看出,有了索引之后在B+树中查找一个记录是十分快速的。假设一个页中最大可以存放 N 行记录,树的高度为 M,则时间复杂度为 :O(M*logN)。

注意:本文参考书籍 MySQL是怎么运行的:从根上理解MySQL,强烈推荐大家购买!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值