深入理解Mysql索引底层数据结构与算法(一)

        在平常开发的过程中,经常会遇到慢查询,如果要优化的话,大部分人第一反应就是加索引,看一下SQL的查询条件是不是走了合适的索引(看查询报告),比如通过Explain来查看。

如果加的索引适当,即便是非常大的数据量,都会将查询的速度至少提高一两个等级

索引为什么会有这么大的作用?以下来简单介绍下(以MySQL为例):

一、索引本质

官方概念:索引是帮助MySQL高效获取数据的排好序数据结构
可以形象的理解书的目录页

比如数据库中存在以下数据:
在这里插入图片描述

在表中任意找一行数据
比如:select * from t where data2=‘22’;

没有任何索引的情况:
        SQL查询的时候,从第一行开始,找到之后放在内存中看一下,是否是要查找的字段,如果不是,继续找第二行…,直到找到要查询的数据。
也就是说,没有索引会全表扫描,逐行逐行去找

        由于数据库里的数据都是放在磁盘上的,如果没有建立索引,那实际上就会逐行从磁盘上拿出来查询数据,然后和查询条件做比对。也就是说查一次数据就和磁盘做了一次IO操作,性能是比较差的(尤其是表数据比较大的时候,就会走很多次的IO操作)。

就上述的SQL,需要查询5次才能找到需要数据

二、二叉树

加了索引的情况:
        索引本质上就是一种数据结构,而数据结构无非就是存数据的一种巧妙的合理设计过的一种结构(比如说二叉树)。
将数据表中的数据合理的放在二叉树中,比如下图:
在这里插入图片描述
假如现在还是要去查上述的SQL:select * from t where data2=‘22’;
        从根节点去查(第一次磁盘IO操作),找到数据56,发现不是需要的数据。由于二叉树的特性:右边的子元素大于父元素,左边的子元素小于父元素。发现要查的22是小于根节点56的,所有下一步应该从左边的子元素去查找,找到数据30(第二次磁盘IO),发现22还是小于30,所以继续在左边的子元素中查找,找到数据22,刚好等于要查找的数据(第三次磁盘IO)

不管是索引还是数据都是存储在磁盘上的。
        实际上二叉树的节点中放了两个元素,一个key,一个value。key就是索引字段,value就是索引所在行的磁盘文件指针。根据指针就可以快速在磁盘中定位到节点的内存,就可以找到需要的数据。

        到这里,我们可以发现不加索引需要5次磁盘IO,而用索引,比如二叉树之后,只用了3次磁盘IO,比不用索引快了很多。
所以说:索引是一种高效的数据结构,对在数据表中过滤数据、查找数据有很大的性能提升

        但是索引本身并不是用二叉树,可能是二叉树有一些问题或者一些场景不适合。比如:将数据data1作为二叉树的节点进行存储。

推荐一个学习数据结构非常好的网站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
常见的数据结构在这里都有演示,比如二叉树(Binary Search Trees)、B+树(B+ Trees)、红黑树(Red-Black Trees)等

在这里插入图片描述
可以看到data1中的数据是有序自增的。现在就用上述网址来把数据放进去看一下效果
在这里插入图片描述
        可以看到,形成的二叉树和我们上面用的结构不太一样,如果放这种单边增长的数据,二叉树为了满足本身右边子树必须大于父节点数据的特性,会使得二叉树成为这种单边增长的结构,也就是说这个时候二叉树就相当于一个链表了。
       这个时候如果去查找数据,也是要一个个查下来,其实相当于索引并没有起到任何所作用(比如查找数据6,不建索引,要查找6次,建了索引还是需要查询6次)。
这个就是二叉树存储索引的弊端,不适合单边增长的序列字段

下面可以来看下其他的数据结构是否可以满足索引的特性。

三、红黑树

HashMap底层其实用的就是红黑树(后续博文会详细讲解)。
现在用数据结构网址来把数据放进去看一下效果
刚插入数据3,效果如下:
在这里插入图片描述
接着就会进行自旋
在这里插入图片描述
有一个自动平衡的过程,两边叶子结点的高度大于1时,会有一个自旋的过程
将所有数据插入之后,效果如下:
在这里插入图片描述
其实,红黑树本质上也是一种二叉树,只不过名称叫做二叉平衡树,会自动平衡。

这个时候我们看下,红黑树是否可以满足索引的特性?
比如查找数据6,不建索引,要查找6次,建了索引只需要查询2次即可(2次磁盘IO即可)
可以看到,红黑树比二叉树的性能会跟高一点

但是其实红黑树也是存在一定弊端的,也不是很适合用于建索引。
(1)树身维护困难
(2)树身频繁自旋
(3)高度不可控(最大的弊端)

       在生产环境中,数据量是非常大的,可能有几万甚至几百万的数据量,红黑树存几行的数据没问题,但是如果数据量过大,比如数据的高度达到了20,查找的数据刚好位于树的最低端叶子结点,那么最少就要经过20次磁盘IO才能找到,其实性能也是无法接受的。
       其实也就是红黑树存储大数据的情况下,树身的高度不可控,表的记录越多,树的高度就越大,当然就是高度不可控,从而也就是磁盘IO的次数也不可控,这就是红黑树最大的一个弊端。

四、B-Tree

       有没有一种数据结构,可以存更多的元素,而且在存储千万级别数据的时候,树的高度是可控的,比如说高度小于5,能不能做到,我们能接受的每次SQL查询做的磁盘IO次数最多不能超过5次。

       如果是要求对红黑树,进行适当的改造,那么怎么做,可以达到上述的效果呢?
       答:原来磁盘上的一个节点分配的空间,只存储一个索引元素。那么我们可以把当时分配的节点空间分配大一点,使得可以存储多个索引元素。也就是使红黑树可以在横向上存储更多的索引数据,那么树的高度就会越小。

经过这样改造之后,其实就得到了一个新的数据结构:B-Tree
现在用数据结构网址来把数据放进去看一下效果
在这里插入图片描述
可以看到,B-Tree把数据全部插入之后,树的高度只有3,如果要查找数据8的时候,三次磁盘IO就搞定了。而相对应,红黑树则需要四次。

B-Tree简单介绍:
1)叶节点具有相同的深度,叶节点的指针为空
2)所有索引元素不重复(B-Tree本质)
3)节点中的数据索引从左到右递增排列
在这里插入图片描述
但是其实B-Tree 也是存在一定弊端的,也不是很适合用于建索引。

每个节点中的data,都存储了key和value。
value可能存储不同的结果,比如说MyISAM存储引擎存储的是磁盘文件指针InnoDB存储引擎的表,存储的是索引所在行的其他所有字段数据,如果一个表的字段过多的话,就会导致data占用的存储空间过大。
若data占用的存储空间比较大,但是整个节点(比如上图,第一个节点存了3个data)的空间是固定的(比如MySQL对于这种大节点推荐的存储空间约16KB),那么就意味着横向可以存储的索引个数就比较少了,高度就会增加,同样不可控(虽然高度要比红黑树矮一点,但是还没有达到理想效果)

备注:
查看mysql文件页大小(16K):SHOW GLOBAL STATUS like 'Innodb_page_size’;

五、B+ Tree

B+Tree(B-Tree变种)
1)非叶子节点不存储data只存储索引(冗余)和指针,可以放更多的索引
2)叶子节点包含所有索引字段
3)叶子节点用指针连接,提高区间访问的性能
在这里插入图片描述

为什么非叶子节点要存储冗余的索引?
因为data元素被挪到叶子节点进行存储,为了构建B+树的整体结构(左边的子元素小于等于父级元素),那么就需要把一些关键的、处于中间的(偏中心的索引值,比如上图的15,20,49等)的索引做冗余,放到非叶子节点中去,方便查找。

MySQL中的索引构建用的就是B+ Tree

六、索引是怎么支撑千万级表的快速查找

在MySQL中,不管是叶子节点还是非叶子节点,大小显示默认都是16KB

那么有人以下疑问
以下图为例:
在这里插入图片描述
(1)问题1:根节点是否可以存储更多的元素?
回答:如果存储的元素过多,那么磁盘一次IO的性能会非常低,没有必要。
MySQL 设置节点为16KB是有它的道理的,16KB可以让MySQL 做一次磁盘IO效率非常高,可以很快加载到内存。
如果超过16KB,比如设置16M,那么一次磁盘IO会非常慢。

(2)问题2:既然所有节点的大小都是16KB,叶子节点中存储了data,那么是不是叶子节点是不是最多只能存储16个元素,最多存不了多少数据?
答:假设用B+ Tree来存储一张大表的索引元素字段,若树的高度为3
可以计算下,这棵树整个存满之后,大概可存储多少索引元素。
假设我们一行数据大小为1K,那么一页就能存16条数据,也就是一个叶子节点能存16条数据;再看非叶子节点,假设主键ID为bigint类型,那么长度为8B,指针大小在Innodb源码中为6B,一共就是14B,那么一页里就可以存储16K/14=1170个(主键+指针)
那么一颗高度为2的B+树能存储的数据为:1170X16=18720条,一颗高度为3的B+树能存储的数据为:1170X1170X16=21902400(千万级条)

那么也就是说高度为3的B+ Tree可以存储两千多万条数据。
要查找任意元素(任何一个叶子节点),只需经过3次磁盘IO就可以查到(查找到节点之后在内存中定位到某个元素耗费的时间和磁盘IO相比可以忽略不计)。
而且一般MySQL表索引的根节点都存在内存中,也就是说只需经过两次磁盘IO就可以查到需要的元素

综上所述,使用B+ Tree存储千万级表示相当easy的。
2000万的表,如果不走索引的话,可能需要查找几十秒甚至几分钟都有可能,因为做的是全表扫描;但是走了合适的索引,可能几十ms或者几百ms就能查找到元素。

一般来说,MySQL的表,创建索引,B+Tree的高度一般是在2~4之间
日常中工作的表一般都是小于等于3的,很少有表超过2000万行,超过2000或3000万行的数据就要进行分库分表了。

更多介绍,可前往深入理解Mysql索引底层数据结构与算法(二)查看

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值