Mysql系列 - 索引

索引是存储引擎用于快速找到记录的一种数据结构

这是MySQL官方对于索引的定义,可以看到索引是一种数据结构,那么我们应该怎样理解索引呢?一个常见的例子就是书的目录。我们都已经养成了看目录的习惯,拿到一本书时,我们首先会先去查看他的目录,并且当我们要查找某个内容时,我们会在目录中查找,然后找到该片段对应的页码,再根据相应的页码去书中查找。如果没有索引(目录)的话,我们就只能一页一页的去查找了。

索引优缺点

优点

  • 减少查询需要扫描的数据量(加快了查询速度)
  • 减少服务器的排序操作和创建临时表的操作(加快了groupby和orderby等操作)
  • 将服务器的随机IO变为顺序IO(加快查询速度)

缺点

  • 索引占用磁盘或者内存空间
  • 减慢了插入更新操作的速度

索引类型

从存储结构划分

  • Btree索引(B+tree,B-tree)
  • 哈希索引
  • 全文索引
  • RTree

从应用层次划分

  • 普通索引:即一个索引只包含单个列,一个表可以有多个单列索引。ALTER TABLE table_name ADD INDEX index_name (column)
  • 唯一索引:索引列的值必须唯一,但允许有空值。ALTER TABLE table_name ADD UNIQUE (column)
  • 主键索引:特殊的唯一索引,不允许为空,只能有一个,一般是在建表时指定。ALTER TABLE table_name ADD PRIMARY KEY (column)
  • 组合索引:在多个字段上创建索引,遵循最左前缀原则ALTER TABLE table_name ADD INDEX index_name (column1,column2,column3)

从表记录的排列顺序和索引的排列顺序是否一致划分

  • 聚集索引:表记录的排列顺序和索引的排列顺序一致,查询效率快,因为只要找到第一个索引值记录,其余的连续性的记录在物理表中也会连续存放,一起就可以查询到。
  • 非聚集索引:表记录的排列顺序和索引的排列顺序不一致,非聚集索引在叶子节点存储的是主键和索引列,当我们使用非聚集索引查询数据时,需要拿到叶子上的主键再去表中查到想要查找的数据。这个过程就是回表。

聚集索引和非聚集索引区别:

聚集索引在叶子节点存储的是表中的数据,非聚集索引在叶子节点存储的是主键和索引列。

比如汉语字典,想要查「阿」字,只需要翻到字典前几页,a开头的位置,接着「啊」「爱」都会出来。也就是说,字典的正文部分本身就是一个目录,不需要再去查其他目录来找到需要找的内容。我们把这种正文内容本身就是一种按照一定规则排列的目录称为聚集索引。

比如要查“玉”字,我们可以看到在查部首之后的检字表中“玉”的页码是587页,然后是珏,是251页。很显然,在字典中这两个字并没有挨着,现在看到的连续的“玉、珏、莹”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到结果所对应的页码。我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为非聚集索引。

索引建立原则

  • 索引并非越多越好,大量索引不仅占用磁盘空间,而且会影响INSERT、DELETE、UPDATE等语句的性能,表数据更改的同时,索引也会进行调整和更新
  • 避免对经常更新的表进行过多的索引,并且索引中的列尽可能少。而对经常用于查询的字段应该创建索引,但要避免添加不必要的字段
  • 数据量小的表最好不要使用索引,由于数据较少,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果
  • 在条件表达式中经常用到的不同值较多的列上建立索引,在不同值很少的列上不要建立索引。比如在学生表的“性别”字段上只有“男”与“女”两个不同值,因此就无须建立索引。如果建立索引,不但不会提高查询效率,反而会严重降低数据更新速度
  • 当唯一性是某种数据本身的特征时,指定唯一索引。使用唯一索引需能确保定义的列的数据完整性,以提高查询速度
  • 在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引,如果待排序的列有多个,可以在这些列上建立组合索引
  • 搜索的索引列,不一定是所要选择的列。换句话说,最适合索引的列是出现在WHERE子句中的列,或连接子句中指定的列,而不是出现在SELECT关键字后的选择列表中的列
  • 使用短索引。如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。例如,有一个CHAR(200)列,如果在前10个或20个字符内,多数值是唯一的,那么就不要对整个列进行索引。对前10个或20个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO 较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值,因此,MySQL 也可以在内存中容纳更多的值。这样就增加了找到行而不用读取索引中较多块的可能性
  • 利用最左前缀。在创建一个n列的索引时,实际是创建了MySQL可利用的n个索引。多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀
  • 对于InnoDB存储引擎的表,记录默认会按照一定的顺序保存,如果有明确定义的主键,则按照主键顺序保存。如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序保存。如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺序保存。按照主键或者内部列进行的访问是最快的,所以InnoDB表尽量自己指定主键,当表中同时有几个列都是唯一的,都可以作为主键的时候,要选择最常作为访问条件的列作为主键,提高查询的效率。另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果

索引失效

  • 使用!=、<> 导致索引失效
  • 类型不一致导致的索引失效:查询时字段值类型必须与表字段类型一致,避免进行类型转换
  • 函数导致的索引失效:避免查询语句中使用函数
  • 运算符导致的索引失效:如果对列进行+,-,*,/,!运算,则索引失效
  • OR引起的索引失效:OR导致索引是在特定情况下的,并不是所有的OR都是使索引失效,如果OR连接的是同一个字段,那么索引不会失效,反之索引失效
  • 模糊搜索导致的索引失效:like语句使用”xxx%”会走索引,使用”%xxx”或”%xxx%”不走索引
  • 尽量少用is null、is not null

慢查询优化

  • 先运行看看是否真的很慢,注意设置SQL_NO_CACHE
  • where条件单表查,锁定最小返回记录表。把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
  • explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
  • order by limit 形式的sql语句让排序的表优先查
  • 了解业务方使用场景
  • 加索引时参照建索引的几大原则
  • 观察结果,不符合预期继续从0分析

Read More:

详解Mysql索引原理及其优化

导致MySQL索引失效的几种常见写法

Mysql索引(一篇就够le)

MySQL索引原理及慢查询优化