自从两年前了解到的索引以来的,就一直想写一篇有关索引的文章。然而我是个拖延癌症患者,一拖就是两年,不愧是我。该篇文章算是本身的笔记,欢迎批评。sql
索引是什么?不少书和文章都会使用图书的目录来类比。目录的目的就是用方便咱们查找具体内容的位置,具体的章节的范围。与此相似,MySQL中索引的用途是帮助咱们加速查询以及排序。数据库
在InnoDB中的索引类型有哈希索引、B+树索引、全文索引。哈希索引在InnoDB中设计为自适应的,不展开讨论。在InnoDB1.12以后有了全文索引,也是应用倒排,还没踩过坑(听说不支持中文),有时间能够研究一下。数组
本篇主要讨论B+树索引。数据结构
学习MySQL的索引,必须得先了解其原理,不然不少问题将一头雾水。下文将讲述索引数据结构的原理,而不涉及其复杂的具体实现。函数
在谈B+树以前,不妨先思考,为何索引能加快查询?为何要用B+树做为索引而不用B树?哈希索引查询复杂度为O(1)为何又不用哈希索引?性能
在了解B树和B+树以前,先了解一下二叉搜索树(BST)和平衡二叉树(AVL)。学习
在顺序存储结构中(如数组),最快的状况是第一个就是目标值,最坏的状况是最后一个是目标值,假设有n个元素,用大O标记法平均的时间复杂度为O(n)。大数据
使用二叉搜索树能够有效的优化搜索时间。利用二叉搜索树的特性左子节点比父节点小、右子节点比父节点大,能够很方便的进行二分查找,有效优化的搜索时间。优化
正常状况下,咱们使用二叉搜索树能够了,但若是出现如下的状况,二叉搜索树反而起不到效果,搜索的平均时间复杂度依旧为O(n)。spa
引入平衡二叉树,深度差不超过1,从而保证不倾斜,或者说更矮,保证其搜索效率。
既然平衡二叉树已经能够加快查询了,但实际上InnoDB并不会使用。在思考B树和B+树的相关问题的时候,离不开一个问题——磁盘IO。索引文件存储在磁盘,假设有平衡二叉树树高30,那么你可能要扫描30次磁盘才能完成搜索。
对于须要磁盘IO的状况,使用平衡二叉树依旧比较糟糕,因此须要引入多路树,即B树和B+树,使得树更“矮”。
若是上述B树改为二叉树,那么树的高度就大了不少,换而言之就须要更屡次的磁盘IO。
B+树是B树的变种。B+树的非叶子结点不存储数据,而且全部的叶子节点以双向链表的形式相连。
如今的索引模型基本都是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 [,...])
复制代码
联合索引会按照创建索引时的顺序,对每一个字段进行排序。即第一个字段排完序,接着排第二个字段,第三...
覆盖索引
在前面提到,非汇集索引搜索记录时还须要经过的主键索引,但若是查找的列刚恰好是联合索引的字段,那就没有必要去再去搜索主键索引,直接取叶子节点值便可,这就是覆盖索引。
为何不用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使用的锁是行锁,但若是在更新时索引失效了,行锁会变成表锁,在开发中应该避免。
经常使用来分组和排序的字段可创建索引。
索引的做用是查询和排序,order by
和group by
是能够用上索引的,若是排序的有多字段,也是按照最左前缀原则。
常常用来查询的字段可建索引。
更新频繁的字段不要创建索引。
频繁更新的字段若是创建来索引,更新时不只更新数据,并且索引的B+树也会发生变化,开销比较大,得不偿失。
选择性小的列不要创建索引。
好比说性别字段,只有男或女或未知,百万数据里只有这三个值,创建索引毫无心义。
索引尽可能使用等值匹配。
尽可能使用覆盖索引。
经过创建索引,能够有效的加速数据库的查询和排序。当谈及的数据库优化时,索引优化确定跑不了。索引的使用有各业界大佬总结的技巧,但不少东西不是绝对的,不能以偏概全,在大数据以及复杂业务下,索引的维护算是玄学,须要不断寻找最佳的索引方案。