尴尬!面试讲不清MySQL索引底层,面试官让回家等通知

同样在 MySQL 索引中又分了几种类型,分别为:

  • B-tree 索引
  • 哈希索引
  • 空间索引
  • 全文索引

下文所有内容均在 InnoDB 的基础上讨论。

为什么要使用索引

①索引可以加快数据检索速度,这也是使用的索引的最主要原因。

②索引本身具有顺序性,在进行范围查询时,获取的数据已经排好了序,从而避免服务器再次排序和建立临时表的问题。

③索引的底层实现本身具有顺序性,通过磁盘预读使得在磁盘上对数据的访问大致呈顺序地寻址,也就是将随机的 I/O 变为顺序 I/O。

这几点不理解就暂时先放着,继续看下文即可,会给你一个满意的解释。

任何事物都存在双面性,既然能提供性能的提升,自然在其他方面也会付出额外的代价:

  • 索引是跟数据共存,因此会占用额外的存储空间。
  • 索引创建和维护需要时间成本,这个成本随着数据量的增大而增大。
  • 索引创建会降低数据的增、删、改的性能,因为在修改数据的同时还需要修改索引数据。

InnoDB 为什么使用 B+Tree 而不使用 BTree

聊到这个问题那就必须得分清楚 BTree、B+tree 的区别,首先来看一下 BTree。

Btree 解析

先来看一下 BTree 的数据结构是怎么样的,这里咔咔给提供一个网站地址,可以看到关于数据结构的一些实现过程:

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

先来看 BTree 的数据结构,下图是咔咔已经将数据填充进去的:

这里有一个陌生区关于 Max. Degree,这个你可以理解为阶,也可以理解为度。

例如现在这个值设置的是 4,那么在一个节点中最多就可以存储 3 条数据,设置为 5那就可以最多放 4 条记录。

现在可以看到目前只插入了 3 条数据:

那么再加一条数据,节点就会进行分裂,这个也就验证了当阶设置为 n 时,一个节点可存 n-1 条数据。

那接着再来插入几条数据看看:

想要达到快速检索数据,那就需要满足两个特性,一个是有序,另一个就是平衡。

从下图中可以看到 BTree 是有一定的顺序性的,平衡性更满足,可以看上文中生成的第一张图。

那么在 BTree 中找一个值是怎么找呢?例如现在要找一个值 9,看一下寻找过程。

首先看到的数据是 4,9 是大于 4 的,所以会往 4 的右节点寻找。继续找到范围在 6 到 8 的节点,9 又大于 8,所以还需要往右节点寻找。

最有一步就找到了数据 9,这个过程就是 BTree 数据结构查找数据的执行过程。

了解到了 BTree 的数据结构后,我们在来看看在 MySQL 中关于 BTree 是如何存储的。

在下图中 P 代表的是指针,指向的是下一个磁盘块。在第一个节点中的 16、24 就是代表我们的 key 值是什么。date 就是这个 key 值对应的这一行记录是什么。

那么此时想要寻找 key 为 33 的这条记录应该怎么找。33 在 16 和 34 中间,所以会去磁盘 3 进行寻找。

在磁盘 3 中进行判断,指针指向磁盘 8。在磁盘 8 中即可获取到数据 33,然后将 data 返回。

那么在这个过程中到底读取了多少条数据呢?在计算之前需要先了解一些知识点。

从 MySQL 5.7 开始,存储引擎默认为 innodb,并且 innodb 存储引擎用于管理数据的最小磁盘单位就是页。

这个页的类型也分为好几种,分别为数据页,Undo 页,系统页,事物数据页。

一般说到的页都是数据页。默认的页面大小为16kb,每个页中至少存储2条或以上的行记录。

那么根据 BTree 数据查找的过程中可以得知一共读取了三个磁盘,那么每个磁盘的大小就是 16kb。

而目前的给的案例寻找了三层,那么三层存储的数据就是:16kb16kb16kb=4096kb。

如果按照一条记录所需内存 1kb,那么这三层的 BTree 就可以存储 4096 条记录。

各位数据库的数据少则几百万,多则几千万数据,那么 BTree 的层级就会越来越深,相对的查询效率也会越来越慢。

这个时候是不是应该思考一个问题,那就是为什么在 Btree 中 48kb 的内存怎么就只能存储 4000 多条记录?

问题就出现在 data 上,要知道在计算数据大小时指针地址和 key 的内存都是没有计算在内的,单单就计算了 data 的内存。

因为在 BTree 结构中,节点中不仅存储的有 key、指针地址还有对应的数据,所以就会造成单个磁盘存储的数据相对很少的原因。

为了解决单个节点存储数据量小的问题,于是就演变出另一种结构,也就是下文提到了 B+Tree。

B+Tree 解析

依然如初看一下 B+Tree 的数据结构。为了方便对比,将 BTree 和 B+Tree 的数据结构放到了一起。

那么可以看到在 B+Tree 中叶子节点是存放了全量的数据,而非叶子节点只存储了 key 值。

咦!这不是就很好的解决了 BTree 带来的问题吗?可以让每个节点存储更多的数据。每个节点存储的数据越多,那么相对的就是树的深度就不会过深。

了解到了 B+Tree 的数据结构后,我们在来看看在 MySQL 中关于 B+Tree 是如何存储的。

从上图很明显就可以看到两点不同:

  • 第一点:B+Tree 所有的数据都存储在叶子节点上。
  • 第二点:B+Tree 所有的叶子节点之间是一种链式环结构。

那么在这个过程中到底读取了多少条数据呢?

如果说 B+Tree 读取数据的深度跟 B-Tree 的深度一样,都是三层,那么同样的道理每个磁盘的大小为 16kb。

那在 B+Tree 中非叶子节点可以存储多少数据呢!一般来说我们每个表都会存在一个主键。

根据三层来计算,第一层跟第二层存储的是 key 值,也就是主键值。

都知道 int 类型所占的内存时 4Byte(字节),指针的存储就给个 6Byte,一共就是 10Tybe,那么第一层节点就可以存储 16*1000/10=1600。

同理第二层每个节点也是可以存储 1600 个 key。

第三层是叶子节点,每个磁盘存储大小同样安装 BTree 的计算一样,每条数据占 1kb。

那么在 B+Tree 中三层可以存储的数据就是 1600160016=40960000。

从这点来看 B+Tree 存储的数据跟 BTree 存储的数据根本就不是一个级别。

所以可以得出结论:

  • B+Tree 能保证检索的数据量相对 BTree 是最多的,而且存储的数据量也是最多的。
  • B+Tree 选择索引时尽量选择所占内存空间小的类型,比如 int 类型。
  • key 所占内存越小,在节点中存储的范围就越多。

Hash 索引

先来创建一个 hash 索引:

alter table user add index hash_gender using hash(gender);

存储引擎使用的是 innodb:

会发现 name 的索引类型还是为 Btree,在 innodb 上创建哈希索引,被称之为伪哈希索引,和真正的哈希索引不是一回事的,这点一定要明白。

在 Innodb 存储引擎中有一个特殊的功能叫做,自适应哈希索引,当索引值被使用的非常频繁时,它会在内存中基于 BTree 索引之上再创建一个哈希索引,那么就拥有了哈希索引的一些特点,比如快速查找。

哈希索引就是基于哈希表实现的,假设对 name 建立了哈希索引,则查找过程如下图所示,哈希表是根据键值对进行访问的数据结构,它让检索的数据经过哈希函数映射到散列表的对应位置,查找效率非常高。

哈希索引存储的是哈希值和行指针,没有存储 key 值、字段值,但哈希索引多数是在内存完成的,检索数据是非常快的,所以对性能影响不大:

  • 哈希索引不是按照索引值排序的,所以也就无法排序。
  • 哈希索引只支持等值操作,不支持范围查找,在 MySQL 中只能只用 =、in 、<>。
  • 哈希索引在任何时候都不能避免表扫描。
  • 哈希索引在遇到大量哈希冲突时,存储引擎必须遍历链表的所有行指针,逐行比较。

B+Tree 跟 BTree 区别

经过了特别漫长的计算、画图现在基本对两者的区别有一定认识了吧!

咔咔在这里进行总结一下:

  • B+Tree 叶子节点上存储的是全量数据(key+data),而非叶子节点只存储 key。
  • B+Tree 在同样的深度下存储的数据是远远大于 BTree 的。
  • B+Tree 每个叶子节点都有指向下一个叶子节点的链接。这样的好处在于,我们可以从任意一个叶子节点开始遍历,获取接下来所有的数据。

B+Tree 适合做索引的原因

B+Tree 树非叶子节点只存储 key 值,因此相对于 BTree 节点可以存储更多的数据,每次读入内存的 key 值就更多,相对来说 I/O 就降低。

B+Tree 树查询效率稳定,任何数据的查找都是必须从叶子节点到非叶子节点,所以说每个数据查找的效率几乎都是相同的。

B+Tree 树的叶子节点存储的是全量数据,并且是有序的,所以说只需要遍历叶子节点就可以对所有的 key 进行扫描,在范围查找时效率更高。

以上就是关于 InnoDB 存储引擎为什么使用 B+Tree 作为索引的解析。

聚簇索引、非聚簇索引区别

聚簇索引、非聚簇索引也被称之为主索引、二级索引。那么如何区分聚簇索引和非聚簇索引呢?

首先看一下 InnoDB 引擎下,创建表生成的文件,可以看到有两个 ibd 文件。

看到这里不知道大家有没有疑问:为什么看有的文章中也会有 frm 文件呢?但是在这里怎么没有呢?

原因是在 MySQL 8.0 之后将源数据都存储到了表空间中,所以也就不存在 frm 文件喽!

都知道这个 idb 文件会存储数据信息和索引信息。那再来看一下 Myisam 存储引擎创建表生产的文件。

从图中可以看到创建一个表会生成三个文件,扩展名分别为 MYD、MYI、sdi:

  • MYD:是表数据文件(保存数据的文件)
  • MYI:是表索引文件(保存索引的文件)

那么就可以得出一个结论:只要数据跟索引存储在一个文件里,那就是聚簇索引,否则就是非聚簇索引。

这个时候就会有人问了,表中有主键的时候,idb 文件中存储的是主键+数据,那么当没有设置主键时怎么办呢?

记住这一句话,在 InnoDB 中,数据插入时必须跟一个索引值进行绑定,如果没有主键那就选择唯一索引,如果没有唯一索引就会选择一个 6Byte 的 rowid。

表中存在多个索引数据是如何存储的

看了上文的解释,有没有产生过一丝疑问,在 InnoDB 存储引擎下,如果存在多个索引,是不是会产生多个 idb 文件。

在 InnoDB 中数据只会保存一份,如果有多个索引,会维护多个 B+Tree,例如:表字段 id,name,age,sex。

id 设置为主键索引(聚簇索引),name 设置为普通索引,那么数据到底会存储几份呢?

不管一个表中设置多少个索引,数据只会存储一份,但是这张表会维护多个 B+Tree。

按照这个案例中 id 为主键索引,name 为普通索引,那么在这张表中就会维护两颗 B+Tree。

id 主键索引跟数据存储在一起,name 索引所在的 B+Tree 中叶子节点存储的是主键 id 的值。

对应的图就是以下两幅图,可以好好地看一下:

最后给大家总结一个点:在 InnoDB 中,一定有聚簇索引,其它索引都是非聚簇索引。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
大家的关注,以后会分享更多的干货给大家!

[外链图片转存中…(img-HgfYQ8iF-1713661609888)]

[外链图片转存中…(img-oN1MxnPl-1713661609889)]

[外链图片转存中…(img-TVLDXgUL-1713661609889)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值