首页 > 技术文章 > mysql学习 索引

daijiting 2020-01-19 16:26 原文

  在平时开发过程中写sql时,我们通常都不太关心sql的性能,只有能给查出来数据,sql的执行速度不是太慢就不会去管它了。但是开发时期的数据量往往都不是太大,很多性能问题只有在生产环境中才会发现,如:数据过多、sql关联了太多的表,使用了太多的join、或者建立了索引,但是索引失效的问题。所以要解决这些性能上的难题,就要去研究mysql最为重要的特性--索引。

一、索引的简介

1.索引的定义

  首先我们来看看官网上,对索引的定义:索引(Index)是帮助MySQL高效获取数据的数据结构。这种数据结构是BTreeB+Tree(我们这里不会去介绍这两种树)。

   详细的来讲,数据库维护着两种东西,一个是用户本身存储的数据,另一个则是满足特定查找算法的数据结构,数据结构可以通过一些特定的方式来指向我们使用的数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。

2.索引的特点

  索引的优点:

  第一点:能够提高数据库检索数据的效率,降低数据库对磁盘的IO读取次数。

    第二点:排序过程中是需要消耗CPU资源的,如果加上了索引,那么就会降低CPU的消耗。

  索引的缺点:

  第一点:索引本身也是一种数据,所以建立了索引会减少磁盘的容量。

  第二点:在进行新增、更新、删除过程中,如果添加了索引,mysql还要对索引进行增删改操作,所以索引对增删改操作是负面的。

3.索引的分类

  a.单值索引

  即一个索引只包含单个列,一个表可以有多个单列索引

  b.唯一索引

  索引列的值必须唯一,但允许有空值

  c.主键索引

  设定为主键后数据库会自动建立索引,innodb为聚簇索引

  d.复合索引(也叫组合索引)

  即一个索引包含多个列,一般来说复合索引的性价比要比单值索引高的多,因此一般建议在创建索引时优先选择复合索引。

4.如何建立索引

  首先我们来看看哪些字段要建立索引:

  a.主键会自动建立唯一索引,

  b.查询中where后面的条件字段应该建立索引,

  c.进行连表查询中,与其他表有外键关系的字段要建立索引,

  d.在有排序和分组的查询中,需要给排序的字段和分组的字段建立索引,

  e.对需要统计的字段,应该加上索引。

 

  在上面我们已经说过了,索引虽然能够大大加快我们的查询速度,但是它也是有缺点的,针对它的缺点我们来看看那些情况不需要加索引:

  a.表中的数据太少不需要加索引,这是因为表记录太少的话查询速度本身就会很快,加上索引提升不大,而且还会多占用磁盘容量。

  b.在对表的操作中,都太多的增删改情况的字段不加索引。对于这种字段,虽然提供了查询速度,但是会降低更新表的速度,因为在进行增删改操作时,mysql也会对索引进行更新。

  c.过滤性太低的字段不要加索引。首先我们来看看什么是过滤性,所谓过滤性指的是这个条件能够过滤多少条记录。举个例子,如user表中的性别(只有男、女性别的情况)字段。

二、查询优化

1.索引失效

  在我们实际开发中,是不是往往会遇到即使加上了索引,sql运行的速度有没有提高很多,面对这样的情况,很可能就是索引了,接下来我们就来看看索引失效的规则:

  a.最佳左前缀法则

  定义:如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。我们来看一个例子

  先创建一张user表,有三个字段

  

 

 

 

  给这三个字段添加复合索引

  

 

   然后我们来看一个sql:

  select * from user where name='小明' and userId='1'

  我们来分析下这个sql,在where的后面有两个条件name和userId,首先根据创建的索引,name是能使用的,而userId就不一样了,在创建的索引中name的后面是age,那么根据左前缀法则,那么userId没有用到索引。那么为什么索引的使用是必须遵守左前缀法则呢,这是和索引创建的数据结构有关,如图。

  根据图我们可以看到,复合索引的创建是一个层级关系,每一层都是由上面的字段指向下一个字段。所以在name_age_userId这个复合索引中,name字段是指向age字段的,age是指向userId字段的,name和userId没有直接关系,因此回到我们的sql,索引只会命中name字段,这就是为什么遵循左前缀法则的原因。

  

 

   b.不在索引列上添加任何操作,如函数、计算、类型转换

  看一个例子:还是上面的那张表,有两个sql:

  select * from user where userId=1;

  select * from user where userId='1'

  这两个sql中有很明显的是,只有第二个才会用到索引,这是因为我们的userId是varchar类型,而第一个sql中,where后面的userId是用了一个数字进行比较,这就会让这个索引列进行了一次类型转换,导致了索引失效的问题。

  c.存储引擎不能使用索引中范围条件右边的列

  看一个例子:

  select * from user where name='小明' and age>17 and userId='1';

  这个sql索引失效的问题也比较简单,首先name能给命中索引,然后age字段由于是一个范围查找,所以索引不能命中age;最后根据左前缀法则,age没有命中索引的,那么userId也无法命中。

  

  接下来的几个索引失效规则都比较简单,就直接列出来:

  d.在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描。

  e.在查询过程中is not null 也无法使用索引,但是is null是可以使用索引的。

  f.在进行模糊查询过程中,like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作。

2.如何正确建立索引

  在介绍了索引失效问题后,我们在以后建立和使用索引时就会有一个很好的思路,下面我们就来总结一下如何正确的建立索引

  a.在创建复合索引中,过滤性越好的字段顺序应该越靠前越好。

  b.组合索引的创建中,有范围查找、不等于、is not null条件的字段应该放在最后。

  c.组合索引的字段中,最好包含where后面中所有的索引字段。

  e.在使用left join 时,小表作为驱动表(主表),大表作为非驱动表,这样做能给使索引最大化的利用。

  f.子查询不要用与join的驱动表,因为索引会失效。

  

推荐阅读