一个常见的面试题是,索引在什么情况下会失效?
通常都是说以下这些情况:
- 查询条件包含or,可能导致索引失效
- 如果字段类型是字符串,where时一定用引号括起来,否则索引失效
- like通配符可能导致索引失效。
- 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
- 在索引列上使用mysql的内置函数,索引失效。
- 对索引列运算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
- 索引字段上使用is null, is not null,可能导致索引失效。
- 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
- mysql估计使用全表扫描要比使用索引快,则不使用索引。
如果光是说一下哪些情况索引会失效,那这篇文章就没什么意义了。我们来分析一下,为什么这些情况会导致索引失效。
1.or条件可能导致索引失效
既然是可能,就又可能生效,有可能失效。 生效的情况:or的各个条件都能走索引,且没有很大的扫描行数。 不生效的情况:or的某个条件不走索引,导致整个sql不走索引。 这个很好理解,就是某个条件不走索引,那么只能进行全表扫描,既然要做全表扫描了,就没必要走索引了。
2.如何字段类型是字符串,where时一定用引号括起来,否则索引失效
比如 select * from t where a=1993; a的类型是varchar,会导致索引失效,因为这个包含了一个隐式函数转化。我们先看一下结果。
这个表结构,数据量是100000条
我们再执行一下第一条语句
可以看出type是All,没走索引。既然包含隐式类型转化,这个转化是在哪儿做的呢?再看一条sql
将条件值做一个类型转化,可以看出type是ref,走了a索引了。
将条件字段做类型转换,type又成了All,不走a索引了。
虽然只论证了一种类型转化,但是先提前说结论吧,如果类型转换是做在值上的,索引不会失效,如果类型转化是在字段上的,则会索引失效。 从设计的思路出发,为什么这样呢?其实很简单,如果是值上的转化,转化的结果是一定的。而在字段上的转化,mysql提前不知道转化的结果是如何的,只有把每个值都进行函数转化之后,和值进行比较,才能知道是否符合条件。 先记住这个设计思路。
这里提一句,如果字段类型是数字类型,传入的参数是字符类型,是可以走索引的,从字符串转化成数字了,这是类型转化规则之一,具体的还有哪些不去尝试了,总而言之,尽量值和字段类型一致,如果传入的和字段的类型一定要不一致,那尽量做好值上的类型转化。
3.like通配符可能导致索引失效。
这里又提了可能失效,什么情况失效,什么时候不生效呢? 当以通配符开头的时候,会导致索引失效。 这就需要理解一下字符串上的索引的特点,字符串的索引,是按照第一个字符进行B+树的构建,在第一个字符相同的区间里,再按照第二个字符进行构建,以此类推。 而like,若以通配符开头,就无法根据第一个字符找到对应的范围,进而继续搜索。 而like的时候,索引生效,就是以字符串开头,比如我是ab%,直接根据索引定位a的范围,再定位到b的范围,这就用上索引了。
4. 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
比如是abc的联合索引,我的条件只有bc,而我数据里符合bc的条件的数据,他们的a七零八落分布在不同地方,那我怎么搜,搜不到啊,这就导致索引失效了。
5. 在索引列上使用mysql的内置函数,索引失效。
这和2一样。
6. 对索引列运算(如,+、-、*、/),索引失效。
这和5一样。
7. 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
这就涉及到MySQL优化器的知识了,什么是优化器,我们简单介绍一下优化器的逻辑。
选择索引就是优化器的工作,而优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句。在数据库里面,扫描行数是影响执行代价的因素之一。
那么优化器是怎么去判断扫描行数的?采用采样统计的方式,采样统计的时候,InnoDB 默认会选择 N 个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数。
这一点,我们也可以通过刚刚的图片得到验证
这个表的数据量是100000行,但是rows是100175,有一些偏差。这个偏差正是因为是采样得出来的,所以会有些许偏差。
为什么要说到优化器呢。因为是否走索引,首先会判断,这几个条件上是否有索引,然后会有一步是数据量的统计。很显然啊,这几个都是查询的范围,优化器会预估一下扫描的行数,采样统计,计算结果集的大小,当然这个结果集的大小是个预估大小。如果说,这个结果集很大,比如占到了数据库数据量的一半,如果我这个索引走的是非聚簇索引,我拿到一半的主键了,然后还要去回表,根据主键id去查找,那会比全表扫描快吗?很有可能比全表扫描慢,全表扫描只要搜索一个树,从叶子节点逐个读过来就好了。那有可能就导致索引失效了。
8. 索引字段上使用is null, is not null,可能导致索引失效。
Mysql innodb 是支持null也走索引的,我认为这个可能导致索引失效的原因,和7一样,这两个条件,是互斥加起来为总数的,很有可能其中一个就失效了。
9. 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
和2一样
10.估计使用全表扫描要比使用索引快,则不使用索引
需要详细了解一下优化器的逻辑,如果要知道准确无误且详细的,看官方文档或者源码吧。
总结
我们刚刚一个个分析过来了,总结一下,有哪些情况会导致索引失效呢? a、字段上做了函数(包含类型转化,字符集转化,内置函数,自定义函数) b、需要搜索的条件,在B+树的分布,存在可以零散分布的情况 c、预估结果集过大,会导致优化器自动忽略索引。
我们再来抽象一下。 a的情况,比如我自定义函数,字段对100取模是10就是我要的搜索结果,那么我要的结果就有可能是零散分布的。总而言之,对字段做函数,规则定了,但是结果是由具体数据而定,那么这个数据就存在零散分布的可能性,也就是b。 c的情况,可以将其概括为优化器自己的优化方案。
也就是说: 1.由B+树的性质决定了,查询的条件,需要是符合数据有序分布的情况,才可能走索引,反之不走索引。 2.由优化器的逻辑决定了,MySQL innodb会根据预估执行速度,判断是否走索引。