MySQL索引凭什么能让查询效率提升这么多?

背景

我相信你们在数据库优化的时候都会说到索引,我也不例外,你们也基本上能对数据结构的优化回答个一二三,以及页缓存之类的都能扯上几句,可是有一次阿里P9的一个面试问我:你能从计算机层面开始说一下一个索引数据加载的流程么?(就是想让我聊IO)面试

我当场就去世了....由于计算机网络和操做系统的基础知识真的是个人盲区,不事后面我恶补了,废话很少说,咱们就从计算机加载数据聊起,讲一下换个角度聊索引。数据库

正文

MySQL的索引本质上是一种数据结构

让咱们先来了解一下计算机的数据加载。缓存

磁盘IO和预读:

先说一下磁盘IO,磁盘读取数据靠的是机械运动,每一次读取数据须要寻道、寻点、拷贝到内存三步操做。网络

寻道时间是磁臂移动到指定磁道所须要的时间,通常在5ms如下;数据结构

寻点是从磁道中找到数据存在的那个点,平均时间是半圈时间,若是是一个7200转/min的磁盘,寻点时间平均是600000/7200/2=4.17ms;性能

拷贝到内存的时间很快,和前面两个时间比起来能够忽略不计,因此一次IO的时间平均是在9ms左右。听起来很快,但数据库百万级别的数据过一遍就达到了9000s,显然就是灾难级别的了。学习

考虑到磁盘IO是很是高昂的操做,计算机操做系统作了预读的优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,由于当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。大数据

每一次IO读取的数据咱们称之为一页(page),具体一页有多大数据跟操做系统有关,通常为4k或8k,也就是咱们读取一页内的数据时候,实际上才发生了一次IO。优化

(忽然想到个我刚毕业被问过的问题,在64位的操做系统中,Java中的int类型占几个字节?最大是多少?为何?)spa

那咱们想要优化数据库查询,就要尽可能减小磁盘的IO操做,因此就出现了索引。

索引是什么?

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。

MySQL中经常使用的索引在物理上分两类,B-树索引和哈希索引。

本次主要讲BTree索引。

BTree索引

BTree又叫多路平衡查找树,一颗m叉的BTree特性以下:

  • 树中每一个节点最多包含m个孩子。
  • 除根节点与叶子节点外,每一个节点至少有[ceil(m/2)]个孩子(ceil()为向上取整)。
  • 若根节点不是叶子节点,则至少有两个孩子。
  • 全部的叶子节点都在同一层。
  • 每一个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <= m-1 。

这是一个3叉(只是举例,真实会有不少叉)的BTree结构图,每个方框块咱们称之为一个磁盘块或者叫作一个block块,这是操做系统一次IO往内存中读的内容,一个块对应四个扇区,紫色表明的是磁盘块中的数据key,黄色表明的是数据data,蓝色表明的是指针p,指向下一个磁盘块的位置。

来模拟下查找key为29的data的过程:

一、根据根结点指针读取文件目录的根磁盘块1。【磁盘IO操做1次

二、磁盘块1存储17,35和三个指针数据。咱们发现17<29<35,所以咱们找到指针p2。

三、根据p2指针,咱们定位并读取磁盘块3。【磁盘IO操做2次

四、磁盘块3存储26,30和三个指针数据。咱们发现26<29<30,所以咱们找到指针p2。

五、根据p2指针,咱们定位并读取磁盘块8。【磁盘IO操做3次

六、磁盘块8中存储28,29。咱们找到29,获取29所对应的数据data。

因而可知,BTree索引使每次磁盘I/O取到内存的数据都发挥了做用,从而提升了查询效率。

可是有没有什么可优化的地方呢?

咱们从图上能够看到,每一个节点中不只包含数据的key值,还有data值。而每个页的存储空间是有限的,若是data数据较大时将会致使每一个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时一样会致使B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。

B+Tree索引

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构。在B+Tree中,全部数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样能够大大加大每一个节点存储的key值数量,下降B+Tree的高度。

B+Tree相对于B-Tree有几点不一样:

非叶子节点只存储键值信息, 数据记录都存放在叶子节点中, 将上一节中的B-Tree优化,因为B+Tree的非叶子节点只存储键值信息,因此B+Tree的高度能够被压缩到特别的低。

具体的数据以下:

InnoDB存储引擎中页的大小为16KB,通常表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也通常为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(由于是估值,为方便计算,这里的K取值为〖10〗^3)。

也就是说一个深度为3的B+Tree索引能够维护10^3 10^3 10^3 = 10亿 条记录。(这种计算方式存在偏差,并且没有计算叶子节点,若是计算叶子节点实际上是深度为4了)

咱们只须要进行三次的IO操做就能够从10亿条数据中找到咱们想要的数据,比起最开始的百万数据9000秒不知道好了多少个华莱士了。

并且在B+Tree上一般有两个头指针,一个指向根节点,另外一个指向关键字最小的叶子节点,并且全部叶子节点(即数据节点)之间是一种链式环结构。因此咱们除了能够对B+Tree进行主键的范围查找和分页查找,还能够从根节点开始,进行随机查找。

数据库中的B+Tree索引能够分为汇集索引(clustered index)和辅助索引(secondary index)。

上面的B+Tree示例图在数据库中的实现即为汇集索引,汇集索引的B+Tree中的叶子节点存放的是整张表的行记录数据,辅助索引与汇集索引的区别在于辅助索引的叶子节点并不包含行记录的所有数据,而是存储相应行数据的汇集索引键,即主键。

当经过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,而后再经过主键在汇集索引中找到完整的行记录数据。

不过,虽然索引能够加快查询速度,提升 MySQL 的处理性能,可是过多地使用索引也会形成如下弊端

  • 建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。
  • 除了数据表占数据空间以外,每个索引还要占必定的物理空间。若是要创建聚簇索引,那么须要的空间就会更大。
  • 当对表中的数据进行增长、删除和修改的时候,索引也要动态地维护,这样就下降了数据的维护速度。
注意:索引能够在一些状况下加速查询,可是在某些状况下,会下降效率。

索引只是提升效率的一个因素,所以在创建索引的时候应该遵循如下原则:

  • 在常常须要搜索的列上创建索引,能够加快搜索的速度。
  • 在做为主键的列上建立索引,强制该列的惟一性,并组织表中数据的排列结构。
  • 在常用表链接的列上建立索引,这些列主要是一些外键,能够加快表链接的速度。
  • 在常常须要根据范围进行搜索的列上建立索引,由于索引已经排序,因此其指定的范围是连续的。
  • 在常常须要排序的列上建立索引,由于索引已经排序,因此查询时能够利用索引的排序,加快排序查询。
  • 在常用 WHERE 子句的列上建立索引,加快条件的判断速度。

如今你们知道索引为啥能这么快了吧,其实就是一句话,经过索引的结构最大化的减小数据库的IO次数,毕竟,一次IO的时间真的是过久了。。。

总结

就面试而言不少知识其实咱们能够很容易就掌握了,可是要以学习为目的,你会发现不少东西咱们得深刻到计算机基础上才能发现其中奥秘,不少人问我怎么记住这么多东西,其实学习自己就是一个很无奈的东西,既然咱们不能不学那为啥很差好学?去学会享受呢?最近我也在恶补基础,后面我会开始更新计算机基础和网络相关的知识的。

我是敖丙,你知道的越多,你不知道的越多,咱们下期见!

人才们的 【三连】 就是敖丙创做的最大动力,若是本篇博客有任何错误和建议,欢迎人才们留言!

相关文章
相关标签/搜索