索引作为提高查询速度的关键手段一直都是很火热的研究对象。而目前开发出来的索引的类型也是很多的,每种索引都有其优缺点,在适当的场景下使用合适的索引将会带来数量级上的的性能提升。

1.索引
1. BTree索引
2. Hash索引
3. 空间索引
4. 索引
5. 聚簇索引
6. 非聚簇索引
7. 覆盖索引

2.索引详解
2.1 BTree索引
BTree索引的组织结构是B+Tree,其中对于每一个节点,节点上有多个key和指向子节点的指针组成,组成形式大概是 point key point key point …这种形式,在叶子节点上是key和指向真实数据的指针还有指向其右边的相邻的兄弟节点的指针。
其中key是自定义组成的组合或者单列索引,查询是怎么使用索引来完成快速的查找的?大概是这样的:当你正确的使用已经定义好的索引后,执行的时候,存储引擎会按照key值进行比较来决定该怎么走下一步直到找到所有的匹配的key然后按照存储在其中的指针来查找到真实的数据。那么处于核心地位的便是key值的比较,这直接关系到了索引是否会正确的起到预期的作用,mysql中对于btree索引的作用的情况有明确的限制:只能作用于由组合索引的从左到右的前缀索引,也就是说如果定义好的索引的顺序是ABC,那么跳跃的使用条件AC就会导致索引无法使用
但是使用前缀索引:A,AB,ABC,都是可以使索引起作用的。当然,对于每个列(A,B,C)你可以使用等值或者范围条件,但是如果使用范围条件
那么其后面的列将无法使用索引,但是当前列和前面的索引都会起作用。
所以使用Btree索引的关键在于组合索引的列的顺序。
2.2 Hash索引
顾名思义,hash索引的存储结构就是按照定义的组合或单列的hash值来映射到一个hash表上,这个hash表上面存储的是指向实际数据的指针。在构造这张hash表的时候冲突解决是链表,在执行查找的时候如果冲突很多的话,数据库将会花费较高的代价来维护这些链表。所以hash表的适应场景比较少。
另外hash所以由于其特性导致了一些限制:
hash索引只能用于等值查找。这很好理解,hash表的储存并不是顺序的,所以给出一个范围的话是没办法对其求出hash值并查找的。
hash索引只能用于全索引匹配,由于在构造hash表的时候传入hash函数的值就是所有的列的值,那么在查找的时候使用部分就肯定不能起作用了。
hash索引不支持排序。
虽然这种索引不常使用,但是在btree索引的基础上通过自定义创建hash索引来获得部分的hash索引的特性可以作为一种优化手段。
步骤如下:在建有索引的表上面添加一列,存储由组合索引所关联的列的hash值(hash函数可以通过CRC32或者其他的函数获得)并且在该列上创建索引。这样在查找的时候就可以更加快速的定位到等值查询时指定的列,解决hash冲突的方法也很简单,就是带上原来的值的条件,这样在出现冲突的时候就可以使用原来的值进行比较。
在如果原来的索引所关联的键是非常长的数值的时候可以这样使用,比如url列上面建立的索引。
2.3空间数据索引(R-Tree)
适用于存储地理数据,mysql的支持并不好。
2.4聚簇索引
这是一种数据的存储方式而非单独的索引类型。他的主要的作用是把逻辑顺序和物理储存顺序统一来加速查找。怎么做到这一点喃,如果使用btree索引的话,把叶子节点上面的key改成实际的表数据就可以做到这一点,它通过btree索引把数据有顺序的组织起来。当你按照索引查找的时候,就可以直接获得表数据而不用进行二次io来读取。
聚簇索引需要一个列来聚集数据,这个列默认是主键,没有的话选中非空的唯一索引,在没有的话会自动生成一个隐式的主键来作为主键索引。
有点很明显,聚簇索引加快了数据的访问速度。特别是在IO密集的时候,这种特性表现的更加明显。但是也具有一些缺点:
首先是索引的维护代价更高了,毋庸置疑把数据和索引放在一起存储在数据的插入和更新的时候需要变动位置,尤其是使用主键聚集的时候,主键插入如果是随机的,那么在更新的时候就没办法快速的定位到其位置,而且需要变动比较多的索引。而且也能发生的“页分裂”的问题也可能导致磁盘空间的进一步扩大。
在使用聚簇索引的时候需要注意的是,插入的主键的顺序,如果是按照顺序插入比如自增,那么插入的效率会比较高,相较之下,使用乱序插入会导致更低的插入效率和更高的索引存储空间,但是顺序主键在高并发的情况下也导致锁的争用,这时候就需要’innodb_autoinc_lock_mode’配置或者表的重新设计。
2.5非聚簇索引
非聚簇索引也叫二次索引,相对于聚簇索引而言的概念。其大概意思是
表数据的储存和索引的存储是分开的,在查找的时候我们先在索引上按照指定的条件找到对应的叶子节点(如果可以使用索引的话),然后按照叶子节点上面存储的“行指针”或者主键值来进行二次查找实际的记录值。
举例来说,MyIsAm储存引擎在存储数据的时候是按照数据的插入顺序存储的并且会生成一个行号来唯一的标记记录,这个行号就是行指针,不是实际指向物理地址的指针,而是从数据存储的相对位置来说的。但是这种方式在数据的位置发生变化的时候需要维护索引。InnoDB的二次索引的策略是主键,在一次查询获得主键之后按照主键值进行二次查找,这样做的好处是不需要为了数据反生改变而维护索引,因为主键值一般不会变,而且InnoDB可以创建自定义hash索引来加速主键的查找。
2.6覆盖索引
如果查询的字段都在一个索引中,那么就称之为“覆盖索引”。覆盖索引存在的意义在于不需要回表查询数据,节省了Io的时间将会是一笔极度可观的收益。
限制在于使用存储索引的结构必须支持存储索引列的值,Mysql中只有btree索引支持。当使用了覆盖索引的时候extra中会出现using index的标志。是否支持或者说是否正确的使用了覆盖索引可以从下面几点来判断。
1.直接的判断:查询的字段是否全部包含在某一个索引中,如果不是,那么就没有使用到覆盖索引。
2.第二,当你的查询的条件中没有使用到索引的时候,或者说使用到的索引和查询的字段所在的索引不是同一个或者呈现包含关系那么也没有使用到覆盖索引。原因很简单,如果你的使用的条件索引无法完成查找那么便会自动从从磁盘中拉取表数据进行比较,从而也就不是覆盖索引了。
对于查找了全部列的查询我们没办法直接转换成使用覆盖查询(除非你在全部列上建立索引),但是可以使用“延迟关联”来完成部分子查询的覆盖索引查询:

select * from table1 where actor_id=1 and name like ‘join%’; 
 建立有索引(actor_id,name),但是并不能使用覆盖索引,我们可以转换成: 
 select * from table1 
 join 
 ( 
 select actor_id from table1 
 where actor_id=1 and name like ‘join%’; 
 ) as t on t.actor_id=table1.actor_id;


这里的子查询会使用覆盖查询,但是效率不一定是得道了提升的,需要看具体情况而定。