索引是一种数据结构,其做用就是用来提升数据查询效率。比较经常使用的比喻就是将其类比为书籍的目录。经过目录能够精确的找到某一章节的内容所在页。数据库
在数据量较小的时候使用索引其实也没有什么意义,即便没有索引须要一条一条遍历数据对于计算机来讲也并不须要太多时间。而一旦数据量较大,要保证咱们能正常的对外提供服务,保证用户使用体验那么索引就是必要的了。数组
索引时一种数据结构,为了应对不一样的场景会有多种实现。在MySQL中主要就是Hash索引和B+Tree。缓存
hash相信你们应该都很熟悉,hash是一种key-value形式的数据结构。实现通常是数组+链表的结构,经过hash函数计算出key在数组中的位置,而后若是出现hash冲突就经过链表来解决(拉链法)。固然还有其余的解决hash冲突的方法。hash这种数据结构是很经常使用的,好比咱们系统使用HashMap来构建热点数据缓存,存取效率很好。性能优化
hash结构存数据首先经过计算key的hash值来肯定其在数组中的位置,若是有冲突就在该数组位置建一个链表。这样很明显有几个问题:markdown
因此咱们能够知道的是hash索引适用于快速选取某一行的数据。数据结构
从名字上看这明显是一种树结构,在大学期间数据结构的课本上树结构是必讲的。树结构是一种特别重要的数据结构,在不少地方都会使用到。运维
上面咱们说到hash索引没法进行范围查询,在树结构中也有一种方便进行有序查询的结构--二叉搜索树。二叉搜索树的结构中要求父节点的值大于左孩子节点而且小于右孩子节点,以下图。函数
上图中二叉树的查询的时间复杂度为O(log(n)),固然要保证O(log(n))的时间复杂度就须要保证二叉树时刻保持平衡。性能
而在MySQL索引中虽然也使用了树结构,可是并非使用的二叉树。由于在数据库中数据最终都是存放在磁盘上的,而若是树的节点过多的话,那么在节点之间转移会花费较多的时间。在MySQL的实现中选择将更多内容放在同一个节点,对同一个节点的操做转入在内存中完成,减小在外存中节点之间转移的次数,以达到提升效率的目的。这就是B+Tree,在B+Tree的实现中一个三层的树结构就基本上能够知足咱们几乎全部的需求了。优化
要了解B+Tree首先就得了解B-Tree,B-Tree是一种平衡树,这里的B指的是Balance而不是Binary,更确切的说B-Tree是一种多路平衡搜索树。
多路平衡搜索树以下图:
这是一种2-3树,意思就是每一个节点存有两个值,同时每一个节点分支数为3,从上图中能够看出来着中结构很适合查询数据。每一个节点的左子树的值都是小于当前节点中最小的值,中间的子树的值全都是在当前节点两个值的中间,而右子树的值全都大于当前节点的最大值。
好比咱们要查找24这个值:
基于上面的流程能够总结B树的搜索:
能够看出其搜索性能至关于在关键字集合内作一次二分查找。从这里看来好像B-Tree没有什么问题,可是须要注意到的是在B-Tree中每个节点都是存储索引关键字以及其表明的具体行数据。而在MySQL中数据库加载数据是以页为单位加载,每一页的大小是固定的(默认16k)。若是每个节点都存储全部的值,那么一页中能存下的节点就会不多,一次查询可能就会进行屡次从内存中去加载数据,致使性能下降。
B+Tree是对B-Tree的一个变种,让其更加适应于进行外部存储文件索引。
二者以前最大的不一样就在于B-Tree的每一个节点都存储全部的数据,而B+Tree须要存储的数据都在叶子节点上,而且增长了顺序访问指针,每一个叶子节点都有指向下一个相邻的叶子节点的地址。这样的结构保证了在一个内存页中能够存下更多的索引节点,而且更加适合进行范围查询。
由于存储引擎负责实现索引,因此接下来讨论索引都是基于MySQL的InnoDB引擎。
聚簇的意思是表示数据行和相邻的键值聚簇的存储在一块儿。一些数据库容许选择具体的某一个索引做为聚簇索引,而在InnoDB的实现中直接将主键索引指定为聚簇索引。若是没有定义主键,InnoDB 会选择一个惟一的非空索引来代替主键索引。若是一样没有定义这样的索引,InnoDB会隐式定义一个主键来做为聚簇索引(row_id)。
聚簇索引实例如图:
在InnoDB中除主键索引外其余都是非聚簇索引,因此也叫非主键索引。非主键索引的叶节点并非存储一行的值,而是存储具体行的主键值。不知足聚簇的定义。
非聚簇索引实例如图:
由上面的两种索引实例图就能够看出来,在查询时若是是经过主键索引查询的话直接查询到数据行而后返回。可是若是是经过非主键索引查询的话首先须要经过该索引肯定主键,而后经过获得的主键从主键索引中查到具体行的数据,后面的经过获得的主键从主键索引中获取数据的过程被称为回表。
回表的过程使得经过普通索引查询较主键索引查询多了一步,在不少状况下效率相对较低。因此在咱们的查询过程当中若是可以仅经过主键肯定数据那最好就是直接使用主键进行查询。
上面介绍了经过非主键查询会有一个回表的过程,可是须要注意的是并非每个查询都存在回表这一步,对于一个普通索引来讲其叶节点存储的是主键的值,那么假设我如今须要的数据也仅仅就是主键的值呢?经过普通索引取到主键的值后就并不须要再到主键索引中查,那么也就不存在回表这一过程了。
上面例子中该非主键索引已经存在了咱们所须要的值,因此该索引也被称为覆盖索引。覆盖索引并非一个固定的结构,可使单索引(一个字段的索引),也可使复合索引,凡是可以直接提供查询结果而不须要进行回表过程的均可以被称为覆盖索引。
不少时候咱们不可能仅仅经过主键来肯定数据,使用普通索引可能会致使低效,因此覆盖索引在平常开发过程当中也是一个很经常使用的性能优化的手段。
固然覆盖索引页并不都是好的,好比我如今创建了一个索引index(a,b)。由a,b两个字段来创建索引,好处已经说过了就是查询ab字段时不会回表,可是若是仅仅经过b字段来查询就没法走这个索引了。创建的索引的索引项是按照索引定义里面出现的字段顺序排序的。
假设如今存在索引index(a,b),那么若是经过a和b来查询可以应用该索引,单独使用a来查询也能应用到该索引,可是若是单独使用b来查询则没法应用到该索引。这就是最左前缀原则,在匹配索引时回匹配索引最左边的n个字段,能匹配上就能够应用该索引。
因为最左前缀原则的存在也就要求咱们在创建索引时可能须要考虑更多的事情。
首先须要清楚的事索引是一种数据结构,创建索引时须要消耗存储空间的,因此索引并非创建的越多越好,而是应该根据需求尽量的减小索引的数量。
而最左前缀原则的存在就使得一个联合索引能够被当成多个索引来使用,固然前提是设计好索引中字段的顺序(实际上最左前缀原则也并非仅仅适用于联合索引,对于字符串索引也使用,字符串索引中最左n个字符至关于联合索引中的最左n个字段)。
好比index(a,b),有了这个索引后咱们就不须要单独为a创建索引,因此在设计联合索引时通常将使用频率较高的字段放在前面。
而后是将区分度较高的字段靠前,区分度就是字段中值的重复率,重复率越低区分度越高。好比性别就不适合做为索引,区分度越高的字段通过一次筛选能过滤掉更多的行。
而后还须要考虑的是字段的大小,因为索引也须要占据空间因此通常选用较小的字段。
MySQL运维内参:MySQL、Galera、Inception核心原理与最佳实践