轻松理解 MySQL InnoDB 索引、B+树索引、查询原理

前言

索引对于DB查询的性能起到至关重要的作用。对于索引如何提升查询性能,通常都会拿查字典来做类比。字典前面会有拼音索引,我们查字典会先查拼音索引,以此来提高查字典的速度。对于这个类比,我们可以思考的更深入点,看看通过拼音索引提升查询速度的根本原因是什么。我们考虑如下几个问题:

1、通过拼音索引能直接定位到字的具体位置吗?

不能,拼音索引只能定位到字所在的页,如果想找到所要的字或者词,还需要在页中再次定位。这个过程和InnoDB的索引设计相同,通过索引只能定位到数据所在页。

2、定位到页后,如何定位到具体字的?

定位到页后,我们一般是从第一个字开始向后查找我们所要的字。或者一眼望过去随机找到所要的字。如果DB也采用这种方式将会比较耗时,因为DB中一页中存储的数据比较多。InnoDB采用了稀疏索引来提升查询页内查找速度。页内数据按主键顺序存放,为主键创建稀疏索引。顾名思义,稀疏索引只为部分键值建立索引,通过索引直接定位到数据所在区间段。区间内再进行查找。这样可以兼顾速度和空间占用。关于稀疏索引我们后面再详细介绍。

3、如果查找“照”这个字,使用拼音索引定位拼音zhao的过程是怎样的?

你肯定不会从A开始往后逐字母查到Z。你应该会先定位到拼音索引最后几页,然后翻到最后一页,在此页中找到Z。提出这个问题,其实我是想出入索引的查找机制,也就是如何高效的在索引中定位到想要的值。按照顺序遍历查找显然是效率最低的查找方式。人类可以通过自己的经验得出Z在索引的最后,但是DB没有这个能力。DB依赖查找算法来定位数据。查找相关算法我们应该再熟悉不过了,InnoDB采用的是B+树的数据结构来组织索引数据以提升查找速度。在下一节会重点介绍相关算法。

通过和字典类比,我们已经大致了解了索引的作用和定位数据的过程。数据库定位数据和用字典查字的过程很像。我们在学习索引的过程中,可以多回顾字典查字的例子。

索引相关算法

二分查找
如果不考虑查找算法,索引键值可以以数组或者链表的数据结构来保存。为了查找更快,我们进一步将索引值按字典顺序保存。这样我们就可以采用二分法快速查找。

二分查找法首先需要将数据按顺序排列。先用查找的值比较中间位置数值。如果查找的数值更小,那么查找的范围缩小一半,只需要再按同样的方式查找中间位置的左半部分。反之,则在右半部分进行查找。

可以看到二分查找法,每比较一次都会缩小一半查找范围,直至找到数值。
我们以下图为例,看看用二分查找法查找 52 所在位置的过程:
在这里插入图片描述

数据库中的检索也会用到二分查找法。我们都知道数据库采用B+数组织索引,但是通过B+树只能定位到你所要的数据所在的页。在页的内部定位数据时,采用的是二分查找法。字典通过拼音索引也只能定位到页,页内需要再次查找到你所要的字。

二叉树和平衡二叉树

二叉树
二叉树左子树键值小于根节点键值,右子树键值大于根节点键值。我们看下面这个二叉树:
在这里插入图片描述
可以看到以上二叉树中查找52的过程也是三步,查找路径是37->56->52。 这和二分查找法完全一样。这是因为这棵二叉树额外满足了其他的一些条件。

还是这些数据,我们还可以构造如下二叉树:
在这里插入图片描述
可以看到这次的二叉树直到第8层才有了左侧子树。1到7层虽然也满足二叉树的定义,但实际上已经退化为链表。用这个二叉树进行查找,效率几乎和顺序查找一样了。

平衡二叉树
第二个二叉树为什么查找效率会低呢?因为这个二叉树失衡了,左轻右重,已经近乎退化为链表。失去了二叉查找树的意义。此时我们引出平衡二叉树。平衡二叉树在二叉树的基础上,还需要满足任意根节点的左右子树高度差不能超过1。上文二叉树的两个例子,第一个是平衡二叉树,第二个不是平衡二叉树。

平衡二叉树的查找效率非常高。因为它的根节点就是二分查找的中间位置数据。每一颗子树都是如此。换句话讲,平衡二叉树已经按照我们想要的二分查找方式构造好了数据结构。

平衡二叉树的数据结构匹配查询方式,所以查询很快。但反过来维护平衡二叉树的数据结构,则需要付出较大的代价。有更新的发生时,可以通过一次或者多次左旋、右旋来保持平衡。本文就不再展开讲平衡二叉树的维护。

B+树
B+树也是一种平衡查找树,它是为磁盘或其他直接存取辅助设备设计的一种平衡树。我们直接看下面的B+树例子,该例子是以学号为索引构造的学生数据B+树:
在这里插入图片描述
可以看到非叶子节点存储的只是索引键值(学号)以及子节点的指针。完整数据存储在叶子节点中。叶子节点中的数据是按照顺序排列的。叶子节点存储完整数据,非叶子节点则是构造的索引值的平衡树。对于给定的索引值,可以通过B+树快速定位到叶子节点。然后在叶子节点内部再次定位到完整的一行数据。

在B+树的维护过程中可能会出现拆分页的现象。由于B+树主要用于磁盘,这意味着大量的磁盘操作。因此如果表的索引很多,在大数据量写入过程中,由于需要大量维护B+树的操作。那么磁盘IO和CPU使用都会大幅上升。

B+树索引
InnoDB 中采用 B+ 树数据结构来存储索引,所以称之为B+树索引。B+树索引又分为聚集索引和辅助索引。每张表有且仅有一个聚集索引。简单来说聚集索引就是以主键构造的B+树索引。而辅助索引则是根据表中索引列构造的B+树索引。

聚集索引
InnoDB中,表中数据按照主键顺序存放。聚集索引就是按照主键顺序构造的B+树索引。聚集索引中,叶子节点其实就是真实存放数据的页。这意味着聚集索引的叶子节点存放完整数据。而非叶子节点存放的则是主键值和子节点的引用。因此通过主键检索非常快,原因是能够直接定位到数据。

唯一索引
与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。

辅助索引
我们经常说为表加索引,实际添加的就是辅助索引。辅助索引的叶子节点并不是数据存储的页。这是为什么呢?首先,B+树的叶子节点是有序的。数据页已经按照主键顺序存储了,因此就无法再按照别的顺序存储。此外,如果我们按照添加的索引字段顺序把完整的数据多存储一份,不但会有很大的冗余,而且当数据发生变化时会有一致性的问题。这也是不可取的。因此,InnoDB采用的方式是,辅助索引的叶子节点中存储的数据只是主键值。叶子节点中的主键值按照当前索引字段的顺序保存。

使用辅助索引查询时,在辅助索引B+树上只能定位到符合条件的主键值。数据库拿到主键值后还需要用聚集索引再做一次查询拿到完整的数据。

稀疏索引
如果我们不是为所有的数据都加上索引,而是以一定的步长去添加索引,那么这就是稀疏索引。
我们看个例子,学生表中有10名学生的数据,按照学号建立索引如下:
在这里插入图片描述
如果我们按照3为步长来索引这10名学生(稀疏索引),那么索引如下:
在这里插入图片描述
可以看到稀疏索引只能定位到某个学生的所在区间段。区间段内需要二次查找。例如我们想要查询学号为5的学生数据,通过稀疏索引我们先定位到学号为4的数据位置,然后再向下查找一次,找到学号为5的学生数据。稀疏索引其实是以时间换取空间,从而达到更好的平衡。

数据库页内部的查找使用了稀疏索引。数据页的一个组成部分叫做 Page Directory。Page Directory 中按顺序存储了数据的主键和数据在页中的相对位置。并不是每条数据都会被索引到 Page Directory。而是以一定步长跳跃的选择数据保存,也就是稀疏索引。通过 B+ 树索引定位到具体的页后,页内部再通过 Page Directory 定位到数据的具体位置。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值