MySQL索引入门指北

自从两年前了解到的索引以来的,就一直想写一篇有关索引的文章。然而我是个拖延癌症患者,一拖就是两年,不愧是我。该篇文章算是本身的笔记,欢迎批评。sql

概述

索引是什么?不少书和文章都会使用图书的目录来类比。目录的目的就是用方便咱们查找具体内容的位置,具体的章节的范围。与此相似,MySQL中索引的用途是帮助咱们加速查询以及排序。数据库

在InnoDB中的索引类型有哈希索引、B+树索引、全文索引。哈希索引在InnoDB中设计为自适应的,不展开讨论。在InnoDB1.12以后有了全文索引,也是应用倒排,还没踩过坑(听说不支持中文),有时间能够研究一下。数组

本篇主要讨论B+树索引。数据结构

B+树

学习MySQL的索引,必须得先了解其原理,不然不少问题将一头雾水。下文将讲述索引数据结构的原理,而不涉及其复杂的具体实现。函数

在谈B+树以前,不妨先思考,为何索引能加快查询?为何要用B+树做为索引而不用B树?哈希索引查询复杂度为O(1)为何又不用哈希索引?性能

BST和AVL

在了解B树和B+树以前,先了解一下二叉搜索树(BST)和平衡二叉树(AVL)。学习

在顺序存储结构中(如数组),最快的状况是第一个就是目标值,最坏的状况是最后一个是目标值,假设有n个元素,用大O标记法平均的时间复杂度为O(n)。大数据

使用二叉搜索树能够有效的优化搜索时间。利用二叉搜索树的特性左子节点比父节点小、右子节点比父节点大,能够很方便的进行二分查找,有效优化的搜索时间。优化

img1

正常状况下,咱们使用二叉搜索树能够了,但若是出现如下的状况,二叉搜索树反而起不到效果,搜索的平均时间复杂度依旧为O(n)。spa

img2

引入平衡二叉树,深度差不超过1,从而保证不倾斜,或者说更矮,保证其搜索效率。

B树和B+树

既然平衡二叉树已经能够加快查询了,但实际上InnoDB并不会使用。在思考B树和B+树的相关问题的时候,离不开一个问题——磁盘IO。索引文件存储在磁盘,假设有平衡二叉树树高30,那么你可能要扫描30次磁盘才能完成搜索。

对于须要磁盘IO的状况,使用平衡二叉树依旧比较糟糕,因此须要引入多路树,即B树和B+树,使得树更“矮”。

img3

若是上述B树改为二叉树,那么树的高度就大了不少,换而言之就须要更屡次的磁盘IO。

B+树是B树的变种。B+树的非叶子结点不存储数据,而且全部的叶子节点以双向链表的形式相连。

img4

如今的索引模型基本都是B+树。

相对于B树来讲,B+树的搜索更加稳定,由于B树有的数据是分布在非叶子节点上的。

B树的叶子节点以链表的形式相连且按照规则排了序,经过B+索引,能够更加方便的获取范围数据。

这也是不使用哈希索引的缘由。虽然哈希索引搜索的时间复杂度为O(1),但大部分时候咱们并不会只查询一条记录,这种时候使用哈希索引就比较乏力了。

汇集索引

汇集索引,亦可称为主键索引。一张表只存在一个汇集索引。

汇集索引是根据其主键做为排序规则的B+树,搜索时根据其主键进行搜索。

其中叶子节点上存储着整条记录的数据。

InnoDB的B+树在磁盘中的存储是以数据页的形式,在树中间进行插入和删除操做涉及列“页分裂”和“页合并”的复杂过程(关于这点我我的也讲不明白,但能够类比AVL树的旋转去理解),十分耗性能,而直接插入尾部是比较快捷的方式,因此在不少的规范中写道,当使用InnoDB引擎的时候,强烈建议用一个与业务无关的自增id做为主键。

此外,删除也是同样的,不少时候会要求作伪删除,不只仅只是为了数据分析,更是为了索引的性能。

非汇集索引

非汇集索引又称辅助索引,以非主键列来创建。非汇集索引能够有多个。非汇集索引和汇集索引的区别在于,非汇集索引的叶子节点并不存储整条记录的数据,而是存储指向的主键的指针。因此,当利用非主键索引进行搜索时,还须要经过主键索引获取整条数据。

单值索引

单值索引就是在数据表单个列上创建单个值。

CREATE INDEX index_name ON table_name(column);
复制代码

与主键索引相似,单值索引按照所指定列排序创建二叉树。当利用单值搜索到目标后,再经过主键索引去读取整条数据。

惟一索引

惟一索引与单值索引区别不大,只是惟一索引的值不会重复。

惟一索引除了能提升一些效率之外,有时也用来保证列的惟一性,如用户的手机号身份证等。这里不作过多赘述。

联合索引

建立联合索引时指定多列便可。

CREATE INDEX index_name ON table_name(column1, columm2, column3 [,...])
复制代码

联合索引会按照创建索引时的顺序,对每一个字段进行排序。即第一个字段排完序,接着排第二个字段,第三...

img5

覆盖索引

在前面提到,非汇集索引搜索记录时还须要经过的主键索引,但若是查找的列刚恰好是联合索引的字段,那就没有必要去再去搜索主键索引,直接取叶子节点值便可,这就是覆盖索引。

为何不用select *,缘由就在此,不只仅是为了减小读取更多列带来的开销,也是为了可以使用上覆盖索引。使用覆盖索引能够减小磁盘IO,有效提升性能。

下文将讲述有关联合索引的更多细节。

最左前缀原则

上文了解了联合索引,知道了联合索引的节点数据是按照建索引的顺序依次排序,由此咱们引出了最左前缀原则,联合索引中,若是要用上索引字段,前面的字段不能跳过。若是上图的例子,假设是找column2=“ccc”的记录,大概的sql以下

SELECT some_column FROM table_name WHERE column2="ccc"
复制代码

这种状况下索引是用不上的,由于索引是先排序的column1,再排序column2,直接经过column2搜索,B+树并不知道怎么搜索。

索引失效

除了上述的最左前缀原则下索引的失效,还有其余索引失效状况。

  • 使用MySQL内置函数运算的列索引会失效。

  • 使用!=,is null,is not null 索引会失效。

    好比你查找id != 500的记录,至关把扫描id<500,以及id >500的记录,本质上全表扫描没啥区别。

  • 范围查询后的列没法使用。

    仍是用上图的例子,假设查询 column1 <= 4的状况

    SELECT some_column FROM table_name WHERE column1 <= 4
    复制代码

    由于column1是排了序的,索引联合索引column1仍是可使用上的,但column1是范围数据,在这范围内column2并不有序。

  • 以通配符开头的模糊查询(LIKE "%string")。

    值得一提的是,LIKE "string%"是能用上索引的,相似于范围查询,查询从string开头的最小字符串stirng开头的最大字符串。知道了LIKE "string%"是能用上索引的就能理解为何LIKE "%string"为何用不上索引了。

还有其余状况的状况,可用MySQL的查询分析器进行分析。

InooDB使用的锁是行锁,但若是在更新时索引失效了,行锁会变成表锁,在开发中应该避免。

索引使用tip

  • 经常使用来分组和排序的字段可创建索引。

    索引的做用是查询和排序,order bygroup by是能够用上索引的,若是排序的有多字段,也是按照最左前缀原则。

  • 常常用来查询的字段可建索引。

  • 更新频繁的字段不要创建索引。

    频繁更新的字段若是创建来索引,更新时不只更新数据,并且索引的B+树也会发生变化,开销比较大,得不偿失。

  • 选择性小的列不要创建索引。

    好比说性别字段,只有男或女或未知,百万数据里只有这三个值,创建索引毫无心义。

  • 索引尽可能使用等值匹配。

  • 尽可能使用覆盖索引。

小结

经过创建索引,能够有效的加速数据库的查询和排序。当谈及的数据库优化时,索引优化确定跑不了。索引的使用有各业界大佬总结的技巧,但不少东西不是绝对的,不能以偏概全,在大数据以及复杂业务下,索引的维护算是玄学,须要不断寻找最佳的索引方案。

相关文章
相关标签/搜索