掌握了这些数据库要点,你也能在面试上游刃有余了,(一)索引

我是方圆,愿你我皆能在面试前,游刃有余!

1. 为什么数据库中要使用索引?

若在数据量较少的时候(几十行数据),那么我们对全表扫描,加载到内存中,效率依然很快,但是如果在数据量很大的时候,就需要用到索引,索引是一种数据结构,能够避免全表扫描,打个比方就像目录一样,能很快的找到我们想要的数据。

1.1 什么样的信息可以作为索引?

  • 主键(毫无疑问的)
  • 唯一键
  • 普通键

2. 索引的数据结构与优化

我们主要想聊的是InnoDB引擎中,B+Tree索引,在此之前,给大家简单介绍一下,B+Tree的优化过程

2.1 二叉查找树

我们先看一种特殊情况

该图显示的是一张链表,那么我们要查询的数据键为17,我们就要遍历整个表,也就相当于全表扫描,要查询7次(O(n))。

如果我们采用平衡二叉查找树,如下图
在这里插入图片描述

我们同样要查询的是数据是17,我们仅仅需要查询3次,这样大大提高了效率(O(logn))

我们先来简单看一下这幅图,图中每个节点都存储了键值和数据。
我们可以很容易的发现它的特点:

  • 每个节点最多有两个子节点
  • 键值和数据同时存在一个节点上
  • 平衡二叉树,左子树和右子树的高度差不能超过1

虽然这相比于遍历全表效率高,但是这只是我们在数据很少的时候的例子,如果数据量变大,由于平衡二叉树每个节点最多有两个子节点的特点,那么树的高度必定会很高,这就会造成数据块(存储数据的单元)很多,造成硬盘与内存IO的次数变多,而IO正是限制数据读取速度的主要原因,为了优化这种情况,就有了B-Tree。

2.2 B-Tree

在这里插入图片描述
上图便是B-Tree的简图,每个节点称为页,它相比于平衡二叉树,在每个节点上能存储更多的数据,而且每个节点对应的子节点个数变多(图上为3个,我们也称它为3阶B树,实际上要远大于3个),这样,就能将树的高度大大降低,从而降低了IO的次数,查找效率因此提高。
我们也不难看出它的特点

  • 根节点至少包括两个子节点
  • 所有的叶子节点都位于同一层

但是到B-Tree这里就没有问题了吗?我想你也知道,不是的。
我们考虑一种情况我们要查询15-33这个范围的数据,B-Tree是不是要找页2,页7,页3,和页8,才能把我们需要的数据读取出来,它进行的IO次数为4,而B+Tree,则2次就行。那么B+Tree是怎么做到的?我们接着看。

2.3 B+Tree

在这里插入图片描述
上图是一张美丽的B+Tree树索引图
我们先来看看它的特点

  • B+Tree在非叶子节点上不再存储数据了,而是只存储键值

为什么要这么做呢?因为,在数据库中,页(节点)的大小是固定的InnoDB中是16KB,其中不再存储数据,意味着能装下更多的键值,键值数量越大,那么树的阶数也就越大,树的形状就更加的矮胖,如此,进行IO的次数减少,从而检索的效率就会增加

  • B+Tree的数据全部存储在叶子节点中,而且是有序的,数据之间以单向链表链接,而页之间又是双向链表链接

这个特点可不得了了!完全可以轻松的应对范围查询,我们假如再查询15-33这个范围,仅需要2次IO,因为页之间是双向链表。
这同样也使得,查询每个数据的效率是相同的(都要从根节点查询到子节点,因为数据都存在子节点上,这就使得查询路径相同)

2.4 简单谈谈Hash索引

InnoDB引擎支持的Hash索引是自适应的,InnoDB存储引擎会根据表的使用情况自动为表生成哈希索引,不能认为干预是否在一张表中生成Hash索引
而支持Hash索引的引擎还有MemoryNDB(官方表如下)
在这里插入图片描述

这里我为什么要提到Hash索引呢?
因为在理论上存在一种情况,使得Hash有更快的查询速度。情况如下:我们查询某个数据的时候,可以根据Hash算法直接定位到这个数据所在的桶,而没有经过B+Tree这样从根节点到子节点的过程,这样更快。

但是存在这种情况,我们为什么还是没有采用Hash索引呢(事物都是两面性的)?

  • 数据库无法根据key的Hash值进行排序,数据是无序的

这一点就要了命了,虽然查询单个数据的时候,很快,但是一到了范围查询,Hash索引便无能为力了,因为无法根据Hash值进行排序,这样就决定了Hash只能进行等值定位(‘=’或‘IN’)

  • Hash索引不能避免表扫描

因为不同的数据,key的Hash值可能是相同的,那么也就是可能被分到一个桶中,在同一个桶中还需要使用Equals方法进行比较,才能找到我们想要的数据,这就发生了表的扫描

  • 如若出现大量Hash值相等的情况,Hash索引不一定比B+Tree快

这也是一种理论上的想法,Hash算法也是在不断的优化,造成大量的Hash值相同也有些让人觉得不可思议。

3. 最后聊聊聚集索引和非聚集索引

  • 聚集索引(聚簇索引):以 InnoDB 作为存储引擎的表,表中的数据都会有一个主键,即使你不创建主键,系统也会帮你创建一个隐式的主键。
    这是因为 InnoDB 是把数据存放在 B+ 树中的,而 B+ 树的键值就是主键,在 B+ 树的叶子节点中,存储了表中所有的数据。
    这种以主键作为 B+ 树索引的键值而构建的 B+ 树索引,我们称之为聚集索引。

  • 非聚集索引(非聚簇索引):以主键以外的列值作为键值构建的 B+ 树索引,我们称之为非聚集索引。
    非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表
    明白了聚集索引和非聚集索引的定义,我们应该明白这样一句话:数据即索引,索引即数据。

3.1 利用聚集索引查找数据

在这里插入图片描述
还是这张 B+ 树索引图,现在我们应该知道这就是聚集索引,表中的数据存储在其中。

现在假设我们要查找 id>=18 并且 id<40 的用户数据。
其中 id 为主键,具体的查找过程如下:

①一般根节点都是常驻内存的,也就是说页 1 已经在内存中了,此时不需要到磁盘中读取数据,直接从内存中读取即可。

从内存中读取到页 1,要查找这个 id>=18 and id <40 或者范围值,我们首先需要找到 id=18 的键值。

从页 1 中我们可以找到键值 18,此时我们需要根据指针 p2,定位到页 3。

②要从页 3 中查找数据,我们就需要拿着 p2 指针去磁盘中进行读取页 3。

从磁盘中读取页 3 后将页 3 放入内存中,然后进行查找,我们可以找到键值 18,然后再拿到页 3 中的指针 p1,定位到页 8。

③同样的页 8 页不在内存中,我们需要再去磁盘中将页 8 读取到内存中。

将页 8 读取到内存中后。因为页中的数据是链表进行连接的,而且键值是按照顺序存放的,此时可以根据二分查找法定位到键值 18。

此时因为已经到数据页了,此时我们已经找到一条满足条件的数据了,就是键值 18 对应的数据。

因为是范围查找,而且此时所有的数据又都存在叶子节点,并且是有序排列的,那么我们就可以对页 8 中的键值依次进行遍历查找并匹配满足条件的数据。

我们可以一直找到键值为 22 的数据,然后页 8 中就没有数据了,此时我们需要拿着页 8 中的 p 指针去读取页 9 中的数据。

④因为页 9 不在内存中,就又会加载页 9 到内存中,并通过和页 8 中一样的方式进行数据的查找,直到将页 12 加载到内存中,发现 41 大于 40,此时不满足条件。那么查找到此终止。

最终我们找到满足条件的所有数据,总共 12 条记录:

(18,kl), (19,kl), (22,hj), (24,io), (25,vg) , (29,jk), (31,jk) , (33,rt) , (34,ty) , (35,yu) , (37,rt) , (39,rt) 。

下面看下具体的查找流程图
在这里插入图片描述

3.2利用非聚集索引查找数据

在这里插入图片描述
查找的流程跟聚集索引一样,这里就不详细介绍了。我们最终会找到主键值 47,找到主键后我们需要再到聚集索引中查找具体对应的数据信息,此时又回到了聚集索引的查找流程。

下面看下具体的查找流程图:
在这里插入图片描述
在 MyISAM 中,聚集索引和非聚集索引的叶子节点都会存储数据的文件地址。

4. 索引这么厉害,是不是越多越好哇?

答案很明显,是否定的。也所谓物极必反,在数据量很小的时候就用不到索引,而在数据量很大的时候,索引越多,维护的成本也就越高,占用的空间也会很大,打个比方,给你一本厚厚的书,目录就占了一大半,基本上把你阅读的兴趣就打破了,索引也是这个道理。

好了,收功!
在这里插入图片描述

同系列

掌握了这些数据库要点,你也能在面试上游刃有余了,(二)锁

参考(下面这篇博客写的是真的好)

刘召考的博客,MySQL索引-B+树(看完你就明白了)

原创文章 56 获赞 19 访问量 6022

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/105959634
今日推荐