互联网科技老男孩的成长之路

面试官:你真的熟悉MySQL及索引吗?聚合索引和非聚合索引呢?

2020-07-24  本文已影响0人  java菲菲

以下这个面试场景,不知道大家熟悉不熟悉:

是的大家可能都知道慢了加索引,那为啥加,在什么字段上加,以及索引的数据结构特点,优点啥的都比较模糊或者甚至不知道。

正文开始

我看你简历上写到了熟悉MySQL数据库以及索引的相关知识,我们就从索引开始,索引有哪些数据结构?

Hash、B+

大家去设计索引的时候,会发现索引类型是可以选择的。

image.png

我<typo id="typo-376" ignoretag="true" data-origin="先">先</typo>聊一下Hash:

大家可以先看一下下面的动图

image.png

注意字段值所对应的数组下标是哈希算法随机算出来的,所以可能出现哈希冲突。

那么对于这样一个索引结构,现在来执行下面的sql语句:

select * from sanguo where name=‘鸡蛋’

可以直接对‘鸡蛋’按哈希算法算出来一个数组下标,然后可以直接从数据中取出数据并拿到所对应那一行数据的地址,进而查询那一行数据, 那么如果现在执行下面的sql语句:

select * from sanguo where name>‘鸡蛋’

则无能为力,因为哈希表的特点就是可以快速的精确查询,但是不支持范围查询。

如果做成了索引,那速度也是很慢的,要全部扫描。

等值查询的场景,就只有KV(Key,Value)的情况,例如Redis、Memcached等这些NoSQL的中间件。

有序数组,它就比较优秀了呀,它在等值查询的和范围查询的时候都很Nice。

不是的,有序的适合静态数据,因为如果我们新增、删除、修改数据的时候就会改变<typo id="typo-890" ignoretag="true" data-origin="他">他</typo>的结构。

比如你新增一个,那在你新增的位置后面所有的节点都会后移,成本很高。

此言差矣,可以用来做静态存储引擎啊,用来保存静态数据,例如你2019年的支付宝账单,2019年的淘宝购物记录等等都是很合适的,都是不会变动的历史数据。

二叉树的新增和结构如图:

image

二叉树的结构我就不在这里多BB了,不了解的朋友可以去看看数据结构章节。

二叉树是有序的,所以是支持范围查询的。

但是他的时间复杂度是O(log(N)),为了维持这个时间复杂度,更新的时间复杂度也得是O(log(N)),那就得保持这棵树是完全平衡二叉树了。

此言差矣,索引也不只是在内存里面存储的,还是要落盘持久化的,可以看到图中才这么一点数据,如果数据多了,树高会很高,查询的成本就会随着树高的增加而增加。

为了节约成本很多公司的磁盘还是采用的机械硬盘,这样一次千万级别的查询差不多就要10秒了,这谁顶得住啊?

同理来看看B<typo id="typo-1379" ignoretag="true" data-origin="树">树</typo>的结构:

image

我们可以发现同样的元素,B+树的表示要比B树要“胖”,原因在于B+树中的非叶子节点会冗余一份在叶子节点中,并且叶子节点之间用指针相连。

其实很简单,我们看一下上面的数据结构,最开始的Hash不支持范围查询,二叉树树高很高,只有B树跟B+有的一比。

B树一个节点可以存储多个元素,相对于完全平衡二叉树整体的树高降低了,磁盘IO效率提高了。

而B+树是B树的升级版,只是把非叶子节点冗余一下,这么做的好处是为了提高范围查找的效率。

提高了的原因也无非是会有指针指向下一个节点的叶子节点。

小结:到这里可以总结出来,Mysql选用B+树这种数据结构作为索引,可以提高查询索引时的磁盘IO效率,并且可以提高范围查询的效率,并且B+树里的元素也是有序的。

额这个这个?有点懵逼呀。

过了一会还是没想出,只能老实交代:这个不是很了解咳咳。

你可以换个角度来思考B+树中一个节点到底多大合适?

B+树中一个节点为一页或页的倍数最为合适。为啥?

首先Mysql的基本存储结构是页(记录都存在页里边):

image.png

所以说,如果我们写 select * from user where username='丙丙’这样没有进行任何优化的sql语句,默认会这样做:

很明显,在数据量很大的情况下这样查找会很慢!看起来跟回表有点点像。

该死,我嘴干嘛。

回表大概就是我们有个主键为ID的索引,和一个普通name字段的索引,我们在普通字段上搜索:

select * from table where name = ‘一方’

执行的流程是先查询到name索引上的“一方”,然后找到他的id是2,最后去主键索引,找到id为2对应的值。

回到主键索引树搜索的过程,就是回表。不过也有方法避免回表,那就是覆盖索引。

!!! 我这个嘴。。。

这个其实比较好理解,刚才我们是 select * ,查询所有的,我们如果只查询ID那,其实在Name字段的索引上就已经有了,那就不需要回表了。

覆盖索引可以减少树的搜索次数,提升性能,他也是我们在实际开发过程中经常用来优化查询效率的手段。

很多联合索引的建立,就是为了支持覆盖索引,特定的业务能极大的提升效率。

最左匹配原则:

例子:

总结

索引在数据库中是一个非常重要的知识点!

上面谈的其实就是索引最基本的东西,N叉树,跳表、LSM我都没讲,同时要创建出好的索引要顾及到很多的方面:

作者:JAVA一方
原文链接:https://blog.csdn.net/A16670113506/article/details/104797955

上一篇下一篇

猜你喜欢

热点阅读