7.1 什么是索引

索引(index)是帮助MySQL高效获取数据的数据结构(有序)
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

索引的优劣分析:

优势:

  1. 提高数据检索的效率,降低数据库的IO成本
  2. 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

劣势:

  1. 索引列也是要占用空间的
  2. 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行 INSERT、UPDATE、DELETE 时,效率降低
7.2 索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种数据结构:

索引结构

描述

B+Tree 索引

最常见的索引类型,大部分引擎都支持 B+ 树索引

Hash 索引

底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询

R-tree(空间索引)

空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少

Full-text(全文索引)

是一种通过建立倒排索引,快速匹配文档的方式。类似于 Lucene,Solr,ES

不同存储引擎对于索引结构支持情况:

索引

InnoDB

MyISAM

Memory

B+tree 索引

支持

支持

支持

Hash 索引

不支持

不支持

支持

R-tree 索引

不支持

支持

不支持

Full-text

5.6版本之后支持

支持

不支持

我们平常所说的索引,如果没有特别指明,都是指 B+树 结构组织的索引

7.3 MySQL索引中的 B+Tree

首先介绍 多路平衡查找树----B-Tree

以一颗最大度数(树的度数指的是一个节点的子节点个数)为5的b-tree为例(每个节点最多存储4个Key,5个指针)

根据索引选字典_主键


键和值绑定在一起放入节点中,通过键大小来查询值B+Tree

最底层才是一个排好序的存储单元,上面部分都只是用于存储键便于查询

以一颗最大度数为4的b+tree为例:

根据索引选字典_主键_02


MySQL索引中的 B+Tree

MySQL索引数据结构对经典的 B+Tree 进行了优化,在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,提高区间访问的性能。

根据索引选字典_mysql_03

7.4 索引分类

分类

含义

特点

关键字

主键索引

针对于表中主键创建的索引

默认自动创建,只能有一个

PRIMARY

唯一索引

避免同一个表中某数据列中的值重复

可以有多个

UNIQUE

常规索引

快速定位特定数据

可以有多个

全文索引

全文索引查找的是文本中的关键字,而不是比较索引中的值

可以有多个

FULLTEXT

在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类

含义

特点

聚集索引

将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据

必须有,而且只有一个

二级索引

将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键

可以存在多个

聚集索引 选取规则:

  1. 如果存在主键,主键索引就是聚集索引
  2. 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
  3. 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引

见图:

根据索引选字典_sql_04


若根据条件建立的索引需要查询其他的值,则需要根据叶子节点关联的主键回到聚集索引查询出整行信息,这种方式就叫 回表查询

根据索引选字典_根据索引选字典_05

7.5 索引语法
  • 创建索引
create [unique | fulltext] index 索引名 on 表名 (列名,...);
# 默认为常规索引
  • 查看索引
show index from 表名;
  • 删除索引
drop index 索引名 on 表名;
7.6 索引的失效情况
  1. 最左前缀法则
    如果索引了多列(联合索引),要遵守最左前缀法则。指的是查询从索引的最左列开始,并且不跳过索引中的列。
    如果跳跃某一列,索引将部分失效(后面的字段索引失效)
    例如:创建的索引关联字段为 profession、age、status
    create index idx_user_pro_age_sta on tb_user (profession,age,status) ;
  2. 根据索引选字典_根据索引选字典_06

  3. 分别进行不同条件的查询
    第一次 对三个关联字段都进行查询,该索引长度为 54
  4. 根据索引选字典_主键_07

  5. 第二次 查询,减少了字段 status,长度为 49,可以推出 status 所占索引长度为 5
    第三次 查询,减少了字段 status 和 age,长度为 47,可以推出 age 所占索引长度为 2
    所以 只含 profession 的索引长度为 47
  6. 根据索引选字典_根据索引选字典_08

  7. 开始验证 最左前缀法则,将最左边 profession 字段去掉进行查询:
  8. 根据索引选字典_根据索引选字典_09

  9. 可以看到查询时没有使用到索引,进行了全表扫描查询的数据,验证了 查询要从索引的最左列开始
    再次进行验证,若含有最左边 profession 字段,但是缺失中间字段进行查询:
  10. 根据索引选字典_mysql_10

  11. 可以看到使用了索引,但是索引长度为 47,说明只使用了只含 profession 的索引,验证了 如果跳跃某一列,后面的字段索引失效
    再进行一个小细节的验证,关联查询字段都包含,但是将查询字段置换顺序,进行查询:
  12. 根据索引选字典_主键_11

  13. 可以看到最后索引长度为 54,说明该索引包含了 三个字段,这说明 where条件 查询时字段顺序与索引关联字段顺序无关,说白了就是,索引只在乎它按顺序关联的字段在不在,不在乎查询时的字段顺序
  14. 范围查询
    联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
    例如:(还是用上面的索引示范)

    所以一般范围最好能加个 = ,这样后面的索引才不会失效
  15. 索引列运算
    不要在索引列上进行运算操作索引将失效
    例如:给字段 phone 加了索引,但对该字段进行了运算操作
explain select * from tb_user where substring(phone,10,2) = '15';
  1. 该索引将会失效:
  2. 字符串不加引号
    字符串类型字段使用时,不加引号,索引将失效
  3. 模糊查询
    如果仅仅是尾部模糊查询,索引不会失效。如果是头部模糊匹配索引失效
    例如:对 profession 进行模糊查询
  4. or 连接的条件
    用 or 分割开的条件,如果 or 前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到
    例如:条件字段中,id 有主键索引,phone 添加索引,age 未添加索引,进行查询

    可以看到,由于 age 没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对 age 也要建立索引
  5. 数据分布影响
    如果MySQL评估使用索引比全表更慢,则不使用索引
    例如:对 user_name 添加了索引,根据条件进行查询

    可以看到,因为字段不为空的很多,所以MySQL选择了全表扫描,而不是走索引;第二次查询满足 null 的很少,所以走索引
7.7 索引的使用细节
  1. SQL提示
    是优化数据库的一个重要手段,就是在SQL语句中加入一些人为的提示来达到优化操作的目地。
  2. 根据索引选字典_主键_12

  3. 覆盖索引
    尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少 select
  4. 根据索引选字典_字段_13


  5. 根据索引选字典_sql_14

using index condition:查找使用了索引,但是需要回表查询数据
using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

  1. 前缀索引
    当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率
    语法:
create index idx_xxxx on table_name(column(n));

前缀长度:

可以根据索引的 选择性 来决定,而选择性是指 不重复的索引值(基数)和 数据表的记录总数 的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

根据索引选字典_mysql_15

  1. 单列索引与联合索引
    单列索引:即一个索引只包含单列
    联合索引:即一个索引包含了多列
    在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引

    减少回表查询
7.8 索引设计原则
  1. 针对于数据量较大,且查询比较频繁的表建立索引
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
  7. 如果索引列不能存储NULL值,请在创建表时使用 NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询