索引的选择是在优化器中做的
优化器的逻辑
在数据库里,扫描行数是影响执行代价的因素之一。扫描的行数越少,意味着访问磁盘数据的次数越少,消耗CPU资源越少。
扫描行数并不是唯一的判断条件,优化器还会结合是否使用临时表、是否排序等因素进行综合判断。
扫描行数怎么判断的?
Mysql在执行语句之前,不能精确的知道满足这个条件的记录有多少条,只能通过统计信息来估算记录数。
统计信息就是索引的“区分度”,如果一个索引上的不同的值越多,这个索引的区分度就越好。而索引上不同的值的个数,称为“基数”。基数越大,索引“区分度”越好。使用show index from t 查看索引的基数。
但是并不准确。
MySQL是怎么得到基数的呢?其实是根据采样统计的方法。为何要采样统计?因为把整张表的数据取出来,确实是可以得到精确的结果,但是代价太高了。采样统计时,InnoDB会默认选择N个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的数据页的个数,就可以得到这个索引的基数。
当变更的数据行超过1/M的时候,会自动触发一次重新索引统计。
可以通过设置参数 innodb_stats_persistent来选择索引统计的方式
innodb_stats_persistent=ON:表示统计信息会持久化存储。默认值为 N=20,M=10innodb_stats_persistent=OFF:表示统计信息只存储在内存中。默认值N=8,M=16
MySQL认为直接扫描主键更快,但是结果并不是这样。
如果MySQL的统计信息不准确,可以使用analyze table t,重新统计索引信息(当explain的rows和实际情况差距较大时)。
索引选择异常和处理
大多数的时候优化器可以找到正确的索引,但是偶尔会出现选择异常的情况。
1、使用force index(x)强制使用索引。MySQL会根据词法分析的结果分析出可能可以使用的索引作为候选项,然后在候选列表中判断每个索引需要扫描多少行。如果force index指定的索引在列表中,就直接选择这个索引。
但是force这个方法在修改索引名字的时候,也需要修改SQL。
2、修改语句,引导MySQL使用我们期望的索引
3、删除多余的索引
