初识MySQL索引原理

为什么使用索引?

据统计,一般的应用程序运行过程中,对数据库读写操作的比例约为10:1,并且常见的写入操作多为简单的I/O操作,一般不会出现性能问题,但是在多表查询或者超大数据量的情况下很容易出现查询时间过长的情况,针对查询速度慢的问题我们应该怎么解决呢?首先想到的就是SQL优化。那如何对SQL进行优化呢?针对不同的情况方法可能会有多种,但是在大多数情况下,利用索引来提高查询效率就是SQL优化的重点。

索引是什么?

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息。索引的一个主要目的就是加快检索表中数据,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构。

这是百度百科给出答案。索引是什么,我们可以将答案总结为三句话:

  • 索引是帮助数据库高效获取数据的数据结构
  • 在关系型数据库中,索引一般存储在硬盘中
  • 建立索引是提高数据获取速度的一种空间换时间的策略

索引对查询速度的提升情况

在介绍之前我们先用一张存有5000W条数据的表来比较下无索引和有索引情况下简单单表查询所用时间。

无索引:

select stu_name from student where stu_number = '2017000003;

在这里插入图片描述

有索引:

创建索引

create index stu_number_index on student(stu_number);
select stu_name from student where stu_number = '2017000003';

在这里插入图片描述

我们发现两次查询耗时相差非常大。

索引本质

建立索引就如同为一本书建立目录,耗费几页纸的空间来提高读者查询某段内容的效率,数据库的索引则是耗费内存或者磁盘空间来换取数据获取的效率。通过索引来不断缩小记录扫描的范围,快速定位到符合条件的纪录。

扫描二维码关注公众号,回复: 11495713 查看本文章

MySQL中常用索引

  • 普通索引INDEX:加速查找
  • 唯一索引:
    • 主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
    • 唯一索引UNIQUE:加速查找+约束(不能重复)
  • 联合索引:
    • PRIMARY KEY(id,name):联合主键索引
    • UNIQUE(id,name):联合唯一索引
    • INDEX(id,name):联合普通索引

索引使用到的数据结构

MySQL索引使用的数据结构

MySQL索引使用的数据结构主要为B+Tree索引哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快,但是MySQL的InnoDB引擎为自适应Hash索引,即不支持用户手动创建Hash索引,主要原因有几点,首先是Hash索引是存储在内存中而不是磁盘中,如果数据量较大,用于维护索引所用到的内存就可能非常大;第二原因是因为Hash索引根据Hash表的数据结构它不适合多条记录的查询,也就是范围查询,第三Hash值是按照顺序排列的,无法加速任何排序操作。综合以上两点原因,InnoDB存储引擎不允许用户手动创建Hash索引,大多数情况下都是使用B+Tree索引。

常用的两种存储引擎对B+Tree的实现方式不同
  • MyISAM存储引擎:
    在这里插入图片描述

MyISAM引擎中索引文件与数据文件是分离的,MyISAM直接将数据记录的地址存放在B+Tree叶节点的data域,在使用索引的时候直接通过key依据B+Tree搜索算法获取到记录地址,然后通过地址获取到相对应的记录。

  • InnoDB:

在这里插入图片描述

在InnoDB引擎中,MySQL数据文件本身就是一个索引文件。树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。(摘自JavaGuide)

补充:树结构为什么不使用二叉搜索树、平衡二叉树或者B-Tree

二叉搜索树:左子树的键值小于根的键值,右子树的键值大于根的键值

在这里插入图片描述

上图的树结构其实也是一颗二叉搜索树,但是这是一种最为糟糕的情况,当我们查询0700时还是需要去遍历所有的节点,这种情况就失去了我们使用索引的意义,它并不能加快我们的查询。

平衡二叉树:一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

在这里插入图片描述

上图是一棵平衡二叉树,根据平衡二叉树的定义我们知道,平衡二叉树相对于二叉搜索树能够降低树的高度,但是依旧不是最后的选择,原因主要有一下几点:

  • 平衡二叉树的数据结构去查询的时候其查询次数依旧取决于输的深度
  • 查询过程中每一层都需要去访问索引文件,我们知道在InnoDB中,这种索引文件应该是存储在磁盘中的,这就意味着每次获取一个节点就需要访问一次磁盘,频繁的IO操作势必导致查询效率的下降,并且每次只能获取到一个节点的数据,而InnoDB 存储引擎的最小存储页的大小是 16K,每次去访问的时候也会造成浪费。

B-Tree:B-Tree是为磁盘等外存储设备设计的一种平衡多路查找树
在这里插入图片描述

由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的利用率,所以B-Tree的性能等价于二分查找。B-Tree能加快数据的访问速度,相对于平衡二叉树来说,B-Tree的高度较低。但是对于B-Tree来说,每一个节点都会保存数据,这样导致在非叶子节点中能保存的指针数量变少,指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低。

B+Tree:B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构在这里插入图片描述

B树也是多叉树结构,一种自平衡的树,而且B+树是从B树演化而来的,那么为什么不使用B+树的前身B树呢?从结构比较来看,B树相比B+树的一个主要区别就在于B树的分支节点上存储着数据,而B+树的分支节点只是叶子节点的索引而已。

为什么B+Tree相对于B-Tree更适合做索引?

  • 读写次数更少,效率更高

    在B+树中,其非叶子的内部节点没有存储数据,因此其内部节点相对B 树更小。如果把所有同一内部节点的key存放在同一盘块中,那么盘块所能容纳的key数量也越多。一次性读内存中的需要查找的key值也就越多。相对来说IO读写次数也就降低了。

  • 时间复杂度固定

    所有的数据均存放在叶子节点,所以无论什么路径总要到达叶节点,所以其时间复杂度是固定的。

  • 效率更高

    由于B+树的数据都存储在叶子节点上,分支节点均为索引,方便扫库,只需扫一遍叶子即可。但是B树在分支节点上都保存着数据,要找到具体的顺序数据,需要执行一次中序遍历来查找。所以B+树更加适合范围查询的情况,在解决磁盘IO性能的同时解决了B树元素遍历效率低下的问题。

什么才是优秀的索引?

建立优秀索引主要考虑以下几点原则

  1. 考虑列的离散性

    对于索引而言,它是用于快速区分数据的一种数据结构,所谓字段数据离散性越好,选择性就越好,就更适合选择作为索引。
    在这里插入图片描述

    我们首先来观察上面的哪个字段更适合来建立索引?学院ID和学生姓名重复度可能都会较高,但是学号是唯一的,所以选择是学号。

  2. 索引的最左匹配原则

    在创建联合索引(stu_name,stu_number,stu_phone_number)时,相当于建立了(stu_name),(stu_name,stu_number),(stu_name,stu_number,stu_phone_number)以下三个索引。

    当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

    实验验证

    创建联合索引

    create index stu_name_number_phone_index on student(stu_name,stu_number,stu_phone_number);
    

    执行查询语句

    顺序:stu_name,stu_number,stu_phone_number

    在这里插入图片描述

    顺序:stu_name,stu_number
    在这里插入图片描述

    顺序:stu_name
    在这里插入图片描述

    顺序:stu_number,stu_phone_number

    在这里插入图片描述

    我们可以清除的看到,当约束条件以前三种顺序排列的时候均用到了索引,而第四种顺序进行查询的时候就无法用到索引。
    在这里插入图片描述

    这种情况依旧用到了索引,是因为这是MySQL会通过优化器进行优化,MySQL中的查询优化器会纠正这条sql语句该以哪种顺序执行效率最高,最后生成真正的执行计划,它会将用到的索引顺序进行优化,(stu_number,stu_name)调换为(stu_name,stu_number)。在实际使用过程中,我们会将选择性更好的列放到where的最左边

  3. 覆盖索引避免回表操作

    SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。

    举个例子:
    在这里插入图片描述

    未索引覆盖:
    在这里插入图片描述

    索引覆盖:
    在这里插入图片描述
    数据。

    举个例子:
    在这里插入图片描述

    未索引覆盖:

    在这里插入图片描述

    索引覆盖:

    在这里插入图片描述性能差距明显,所以在写SQL或者设计索引时尽量考虑到索引覆盖的问题。

猜你喜欢

转载自blog.csdn.net/qq_41345281/article/details/107762105