MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
可以得到索引的本质:索引是数据结构。

索引的优势:(1)类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本;(2)通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。

索引的劣势:(1)虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,
都会调整因为更新所带来的键值变化后的索引信息;(2)实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。

MySQL的索引所使用的数据结构是B+树,而不是B树。B树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的指针,数据只放在叶子节点中。

选择B+树的原因是:在有限的内存下:

          1) B+树的磁盘读写代价更低 
         B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 
         2) B+树的查询效率更加稳定 
       由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

         3)B+树的元素遍历效率高

            B树虽然提高了IO性能,但是并没有解决元素遍历效率低下的问题,因为遍历也要一个一个的查找。但是B+树就不同了,因为叶子节点互相用指针连接,所以只要顺着第一个查找到的叶子节点,就可以利用指针去遍历剩下的元素了。而且,在数据库中基于范围的查询是非常频繁的,也就是元素遍历操作非常频繁,所以B+树这种结构就会大大优于B树的结构。

 

聚簇索引与非聚簇索引:

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。术语‘聚簇’表示数据行和相邻的键值聚簇的存储在一起。

按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不用从多个数据块中提取数据,所以节省了大量的io操作。所以,为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议用无序的id,比如uuid这种。
对于mysql数据库目前只有innodb数据引擎支持聚簇索引,而Myisam并不支持聚簇索引。
由于数据物理存储排序方式只能有一种,所以每个Mysql的表只能有一个聚簇索引。一般情况下就是该表的主键。MySQL中只有聚簇索引真正存储数据。其他的索引只记录指向数据的指针(非主键索引存储的数据有另一种说法是存储数据的主键,然后再去主键索引查询。至于到底存储的是啥,还有待日后研究。确定是,存储的是主键值,要回查聚簇索引才能得到数据),并不存储实际的数据。

MySQL索引分类:

  1. 单值索引        即一个索引只包含单个列,一个表可以有多个单列索引
  2. 唯一索引        索引列的值必须唯一,但允许有空值
  3. 主键索引        设定为主键后数据库会自动建立索引,innodb为聚簇索引
  4. 复合索引        即一个索引包含多个列

需要创建索引的情况:

  1. 主键自动建立唯一索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  4. 查询中统计或者分组字段

不要创建索引的情况:

  1. 表记录太少
  2. 经常增删改的表或者字段
  3. Where条件里用不到的字段不创建索引
  4. 过滤性不好的不适合建索引

单表查询索引优化:

  1. 全值匹配我最爱:如果where中有多个等值判断条件,则应该创建这些条件属性的复合索引,这样效率最高。
  2. 最佳左前缀法则:如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
  3. 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
  4. 存储引擎不能使用索引中范围条件右边的列
  5. mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
  6. is not null 也无法使用索引,但是is null是可以使用索引的
  7. like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作
  8. 字符串不加单引号索引失效

关联查询索引优化:

  1. 保证被驱动表的join字段已经被索引:驱动表是join左边的表,被驱动表是join右边的表(忽略right join,因为可以由left join替代)
  2. left join 时,选择小表作为驱动表,大表作为被驱动表。
  3. inner join 时,mysql会自己帮你把小结果集的表选为驱动表。
  4. 子查询尽量不要放在被驱动表,有可能使用不到索引。(因为子查询出来的表是一个不存在的表,无法创建索引)
  5. 能够直接多表关联的尽量直接关联,不用子查询。

子查询索引优化:尽量不要使用not in  或者 not exists,用left outer join ... on  ...  where xxx is null 替代。因为用not in(子查询)                                 的话,子查询出来的结果是一个不存在的表,只能全扫描,无法使用索引。而后面的写法可以使用索引。

排序查询索引优化:对于,排序查询的优化,关键就是使用索引现成的排序,而避免出现using filesort。有三条原则:

  1. 如果查询没有过滤条件,比如where子句或者limit等,就不会使用索引,即使有索引的列组合与排序的列组合一模一样。
  2. 如果排序的列组合的顺序与索引的顺序不同,也会使索引无效,导致using filesort。
  3. 如果排序的不同列之间的排序顺序不同,比如一列是升序排序,另一列是降序排列,那么也会使索引无效,导致using filesort。但是同时是升序或者降序是可以使用索引的。(其实,这点我是无法理解的,为什么排序顺序不同就无法使用索引,这可能与复合索引的具体实现有关) 
  4. 因为复合索引的其中一个列作为范围条件的话,后面的列就不在会使用索引,所以当范围条件和group by 或者 order by  的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。

分组查询索引优化:group by 使用索引的原则几乎跟order by一致 ,唯一区别是groupby 即使没有过滤条件用到索引,也可以直接使用索引。(因为group by 分组就是先排序再分组的)

什么是覆盖索引?简单说就是,select 到 from 之间查询的列 <=使用的索引列+主键。通俗的说就是,你想查询哪些列,就在select子句中写明,不要用*代替,这样做的话,即使在不能使用索引的情况,也能勉强用上一点索引,来提升一点速度。(这点,我也是不理解的,很懵逼。。,为啥呢?)