MYSQL为什么会选错索引?

MYSQL执行查询SQL时,主要是Server层负责的,Server层有几大模块:连接器、分析器、优化器和执行器。其中选择索引的任务,是交给了优化器,那么问题就变成了优化器为什么会选错索引。

错误原因主要有两点:

1、使用普通索引时,有回表,那么优化器会考虑这个性能损耗,最后放弃普通索引,选择主键,因为优化器认为扫描主键更快。

2、判断要扫描的行数时判断错误。
MYSQL执行语句之前,并不能准确知道满足条件的数据有多少行,只能通过统计信息的预估来判断。
这个统计信息,是指索引的“区分度”,也就是索引一共有多少种值,简单理解就是把某一列distinct或者group by之后得到的值得数量。
MYSQL是怎么算出这个“区分度”的数值呢?答案是采样法。
具体原理是MYSQL默认读取几个数据页,分析数据页中的“区分度”,获得一个数字,然后再乘以索引占用的页面数,就得到了最终的值。采用这种方式,数据很难和实际数据一致。并且表中的数据是在不停变化的,索引统计信息在满足一定阈值才会更新,也就是说这个统计信息也不是每插入一条数据就实时更新。

既然是预估,那就可能不准,不准就导致最终索引选择错误。
你可能也会问:为什么是预估?因为如果要精确计算要扫描的行数,要全表扫描,一行一行的统计,这样代价太大,性能非常低。

当索引的统计信息不准了,我们也可以手动更新:analyze table 表名。
索引选择错误,我们也可以手动指定SQL要使用哪个索引:select * from 表名 force index(索引名) where a = … and b = …
索引选择错误,如果错选的索引是多余的,可以删掉,留下你希望使用的索引,那么优化器一般会使用对应的索引。