Mysql索引到底是什么?

·  阅读 88

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

mysql已经是java后端必备的知识点,不管是面试还是日常工作,多多少少都会和mysql打交道。

mysql作为毕考察的知识点一定是索引和mysql的优化。那么怎么明明白白讲清楚索引到底是什么,

我也面试过很多家,都会问道索引的问题,日常开发中很多慢查询都是因为没加索引,或者索引加的不对导致最终 查询没走索引。下文是以我自己的理解来阐述索引。

索引

先说说为什么需要索引,要索引的目的是什么,没有索引和有索引的区别是什么?

直观的感受到就是,如果一个表给某个字段加了索引,根据这个字段的查询也就是where条件后面跟索引字段的查询效率高,耗时短。

比如你点击网站上的一个查询按钮,这个查询调用的后台的接口,后台的接口最后调用的是mysql你写的查询语句。

如果没加索引你需要等一分钟才会有数据返回,如果加了索引一秒之内就会有数据返回展示。这里面只针对索引对查询效率的影响,不讨论缓存和其他技术。

说了这么多,为什么加索引,因为加了索引查询效率快。仅此而已...

好比一本书没加索引前这本书是没有章节和目录的。看过电子书没,如果一本几百万字的小说,是没有目录的,你需要找到一个章节的内容,那么只有一行一行找,直到找到为止。

如果有目录那么我就先去找目录,找到目录对应的页数直接翻到对应的页数,索引就像书的目录一样是提高查询效率的。

为什么加索引就查询快了

索引的本质是一种:数据结构,复杂的查询借助这种数据结构来帮助我们提高查询效率。

比如现在一张表的数据存了十万条数据身份证信息,我需要查询到我自己的身份证信息,身份证没加索引那么只有在这十万条数据一条一条的找,知道找到为止,最坏的结果是找十万次,时间复杂度就是O(n),慢的能让你怀疑人生。

之前我在做K12教育的时候,我们有一个省的K12学生数据,我们有一个登录系统是需要通过学生的身份证登录的。开始做完在测试环境测试登录正常没任何问题,就开心的听着dj上线啦!

上线的第二天客户反映有的身份证登录不了,一直在转圈,开始我们都没往索引这方面想,都问客户要身份证,然后我们自己去登录,发现有时候很快,有时候贼慢,又是找网络问题,又是排查账号问题,最后跟踪到执行的sql的时候,查看索引执行计划发现是全表扫描,就是上文说的一条一条的在查找。立马给身份证这列加上索引,线上问题立马恢复,几乎秒级别能登录成功。

不管你是怎么的大神还是新手,在索引问题上我想都栽过跟头。

那么这个例子为啥身份证加上索引就变快了呢?

我们可以这样想,给身份证这列加了索引,就加了一种数据结构给这张表,就好比给这张表加了目录,而且这个目录是递增的有序的就是身份证号码按字典序排是从小到大的。

假设我们在这里身份证号码是一到十万的数字,举例数字便于大家理解,那么这张表就乱序的存了这十万个数字(身份证号码),如果我给这列身份证号码字段加了索引,就是加了一种数据结构,这个数据结构就是从一到十万按顺序排列的身份证号码(1,2,3,4,5.....100000)

那么我们要找身份证号码为1000的数字,之前没有索引我们需要依次一个一个找,如果这个1000的因为是乱序排列的恰好排在最后一行,那么你很倒霉需要找十万次。

那么有了索引再查询身份证号码为1000的,因为是有序,你可以通过二分查找,二叉树查找等方法可以高效的查询到身份证号码为1000的数据。

所有就是加了一种叫索引的数据结构,以便我们更快速的查询出我们需要的结果。

那么接下来说说我们常见的索引数据结构...

常见索引的数据结构

  • 有序数组:就是按递增顺序排列所有值的数据结构

比如上文我举例的从1到100000的按递增排列的身份证号码的例子,就是有序数组。

在身份证不会变更的情况下,这种查询会很快,有各种二分法,二叉树查找等方法快速的查询出结果。

但是现实生活中我们的数据一般会有更新和删除操作,我们都知道数组在插入和删除的时候效率会比较低,所以这种有序数组只适合在表数据不变的情况下使用。

  • 哈希表:通常以键 - 值(key-value)存储数据的结构

不同的key存储不同的value,key的哈希结果用数组存储,用特定的哈希函数算法将key算出哈希值,放在数组的位置上,这样就会出现一个问题,当两个不同的key但是通过哈希算法算出来的哈希值一样的时候,就会出现哈希碰撞

一般解决哈希碰撞可以再用另一个哈希算法算结果存储,或者直接往数组后面找,找到空余的位置放,比较经典的是拉链法,当出现相同的哈希值的时候,在对应的哈希值的位置上拉出一个链表,相同的哈希值的时候,直接放到这个链表的最后的位置,拉链法如图:

image.png

左边abcdef是key通过哈希算法算出来的值,为数组结构。

如图如果key为li和ni的哈希值的结果都是a的时候就从数组a的下标值拉出一个链表,分别存li和ni的value。

这样结合数组的随机查找,链表的顺序查找可以相对快的查询出需要查询的值,但是因为链表不是递增或者有序的,冲突的时候是在链表最后追加的,是需要全链表查找的,查找时间复杂度比较高。

所以,如哈希表这种数据结构的索引只适合等值查询的场景,比如Memcached和一些NoSQL引擎。

  • B+ 树索引模型:Mysql的InnoDB引擎所用的索引数据结构

了解B+树之前先需要了解一种简单的树形结构,二叉搜索树。

二叉树:有父节点,左子树,右子树。

二叉树搜索树:和二叉树结构一样,但是在节点存储值的时候必须满足:左子树小与父节点,右节点大于父节点,这种情况必须满足所有子树,意思是它的左、右子树也分别为二叉搜索树。

那么以这种结构存储数据的时候,还是之前的身份证号码存储我们就把所有的身份证信息按二叉搜索树的格式存储。

从根节点往下找,当发现找的值比跟节点大时候,往右节点找,当发现要找的值比当前节点小的时候,往左节点找,依次类推直到找到目标值。

在实际的数据结构中,我们用的不是二叉树,是个多叉树,多叉树就是每个节点有多个节点,节点之间的大小保证从左到右递增的顺序。

当然你会有疑问,为啥不能是二叉搜索树呢?因为二叉搜索树查询效率肯定比多叉树效率高。

效率高但是只有两个节点,保存相同的多的值的时候,这个树的高度就会很高,举个例子,同样保存10个值,按二叉树的结构存,你最少需要三层树的高度。那么如果我一层可以放多个值,我放五个,那么只要一层高度就可以了。

在实际的mysql的时候,我们索引也是一种数据结构,实际索引的数据结构我们保存在磁盘上的,保存在内存中会有丢失的风险,那么每从一层下探到下一层的时候,就是一次IO操作,树越高这种IO操作就会越多,IO操作越多查询就会变慢。

为了让查询尽量少走读磁盘IO操作,就必须在查询过程中尽量少的访问树的高度。二叉树这种结构树会很高,不满足实际需求,我们可能更需要一种N叉树。

这种N叉树我们暂时认为是B树,那么B树事实上是一种平衡的多叉查找树,也就是说最多可以开N个叉(N>=2),整个B树的存储是和二叉搜索树的特点一样,是有顺序存储的,搜索过程相当于在一个B树上做一次二分查找。

为了保持整个树的存储是按二叉搜索树的特点存储的。在对数据新增和删除的时候是对树进行一个分裂、合并、转移等操作以保证B树的所有数据是符合B树的特点的。这在mysql索引就会出现页分裂,和页数据合并。

InnoDB引擎所用的索引数据结构:每个索引都需要建一个这种数据结构。

在B树上做了升级,所有值还是有顺序的,非叶子节点,也就是不是最底层的节点全部存储的是索引值,叶子节点存储的是索引对应的实际数据值,所以查找的时候必须找到叶子节点才会有数据返回。

每个叶子节点实际数据块增加了一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B树。

这个涉及到查询的机制,mysql查询的值的时候不是按一条条返回的,是按一页一页的数据返回的,一页数据的大小为4K,如果查询的数据和上次查询的数据相邻,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。如图: image.png

总结

本文简单介绍了什么是索引,为什么需要索引,索引常见的数据结构,mysql的InnoDB引擎用的是一种什么样的数据结构作为索引的。以及InnoDB引擎是怎么利用索引存储数据的。

分类:
后端
标签:
分类:
后端
标签: